From 48d5a2184d2174554364288e48f67b0d8a92a613 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:05:58 +0100 Subject: [PATCH 01/36] refactor(compiler)!: remove the python compiler engine and its dead branches Every native stage is shipped, so the python engine switch, the NativeStage members it gated and their native-answer counters no longer select anything. SQLBUILD_COMPILER_ENGINE=python and --compiler-engine python now stop with an error that explains the removal. Python branches that only the python engine reached are deleted: JSON SQL-test planning and chain resolution, Python promotion conflicts, the whole-project contract fallback and the attachment else-branches. Native deferrals still hand their inputs to the Python helpers. The compiler differential now compares native with native-preview plus the goldens, and the phase-CPU ratio jobs compare against the base commit. --- .github/workflows/compiler-differential.yml | 8 +- .github/workflows/verify.yml | 51 +-- Makefile | 15 +- crates/README.md | 29 +- .../bindings/_helpers/analysis/sql_tests.rs | 20 - scripts/compiler_differential/README.md | 56 ++- .../_helpers/running/native_fallbacks.py | 6 +- scripts/compiler_differential/constants.py | 5 +- .../main/differential.py | 4 +- .../_helpers/type_normalization.py | 18 +- .../_helpers/compile/target_writer.py | 16 +- .../cli/commands/_helpers/entry/parser.py | 3 +- .../cli/commands/_helpers/entry/parsing.py | 17 +- .../compile/_helpers/attachment/audits.py | 3 - .../compile/_helpers/attachment/core.py | 39 +- .../_helpers/attachment/declaration_scope.py | 3 - .../compile/_helpers/attachment/functions.py | 4 - .../_helpers/attachment/model_config.py | 5 - .../attachment/scope_relationships.py | 10 - .../compile/_helpers/attachment/sql_tests.py | 172 ++------ .../compile/_helpers/attachment/target.py | 4 +- .../compile/_helpers/diagnostics/recovery.py | 23 +- .../diagnostics/sql_analysis_opt_outs.py | 21 +- .../_helpers/macro_bridge/call_store.py | 3 - .../_helpers/macro_bridge/expansion.py | 7 +- .../_helpers/native_stages/assembly.py | 50 +-- .../_helpers/native_stages/sql_tests.py | 9 +- .../compile/_helpers/refs/references.py | 3 - .../_helpers/render/cursor_intrinsics.py | 4 - .../compile/_helpers/render/declarations.py | 7 +- .../_helpers/render/macro_argument_values.py | 3 - .../compile/_helpers/render/macros.py | 5 +- .../compile/_helpers/render/parameters.py | 85 +--- .../compile/_helpers/render/templating.py | 4 - .../compile/_helpers/scenarios/core.py | 3 - .../compile/_helpers/sql_tests/core.py | 3 - .../compile/main/_assemble_project.py | 6 +- .../compile/main/expand_sql_with_spans.py | 6 +- .../contracts/_helpers/native_contracts.py | 12 +- .../contracts/_helpers/native_promotion.py | 19 +- .../compiler/contracts/_helpers/promotion.py | 112 ------ src/sqlbuild/compiler/contracts/constants.py | 2 - .../main/_evaluate_native_model_contracts.py | 6 +- .../main/_native_promotion_conflicts.py | 9 +- .../contracts/main/promotion_conflicts.py | 13 +- .../compiler/contracts/main/validate.py | 29 +- .../_helpers/native/declaration_files.py | 5 - .../discovery/_helpers/native/model_files.py | 3 - .../compiler/frontier/_helpers/engine.py | 17 +- .../frontier/_helpers/native_frontier.py | 7 +- src/sqlbuild/compiler/frontier/constants.py | 23 +- .../frontier/main/_compile_frontier.py | 13 +- .../frontier/main/parse_compiler_engine.py | 10 + src/sqlbuild/compiler/frontier/types.py | 20 +- src/sqlbuild/compiler/lineage/main/columns.py | 15 +- .../main/run_with_macro_bridge.py | 4 +- .../_helpers/sql_tests/native_planning.py | 378 +----------------- .../main/execution/sql_test_artifacts.py | 25 -- src/sqlbuild/executor/testing/types.py | 4 - 59 files changed, 260 insertions(+), 1196 deletions(-) delete mode 100644 src/sqlbuild/compiler/contracts/_helpers/promotion.py create mode 100644 src/sqlbuild/compiler/frontier/main/parse_compiler_engine.py delete mode 100644 src/sqlbuild/compiler/planner/main/execution/sql_test_artifacts.py diff --git a/.github/workflows/compiler-differential.yml b/.github/workflows/compiler-differential.yml index 140548c944..843a4abb22 100644 --- a/.github/workflows/compiler-differential.yml +++ b/.github/workflows/compiler-differential.yml @@ -37,14 +37,8 @@ jobs: run: uv python install 3.12 - name: Install dependencies run: uv sync --locked --all-groups --all-extras - - name: Compare dense projects with and without custom rules on the preview engine + - name: Compare dense projects with and without custom rules (native vs native-preview) env: MODELS: ${{ matrix.models }} run: make compiler-differential-dense COMPILER_DIFFERENTIAL_DENSE_MODELS="${MODELS}" - - name: Compare dense projects with and without custom rules on the shipped engine - env: - MODELS: ${{ matrix.models }} - run: >- - make compiler-differential-dense COMPILER_DIFFERENTIAL_DENSE_MODELS="${MODELS}" - COMPILER_DIFFERENTIAL_ARGS="--engines python native" diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 7289df00c6..1ab78f7234 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -303,7 +303,7 @@ jobs: run: uv python install 3.12 - name: Install dependencies run: uv sync --locked --all-groups --all-extras - - name: Compare fixtures, examples, generated seeds and the failure corpus (python vs native-preview) + - name: Compare fixtures, examples, generated seeds and the failure corpus (native vs native-preview, goldens) run: >- make compiler-differential COMPILER_DIFFERENTIAL_ARGS="--stage-captures --require-discovery-coverage @@ -318,12 +318,6 @@ jobs: path: ${{ runner.temp }}/compiler-differential-wheel-sites.json if-no-files-found: warn retention-days: 30 - - name: Compare fixtures, examples, generated seeds and the failure corpus (python vs shipped native) - run: >- - make compiler-differential-shipped - COMPILER_DIFFERENTIAL_ARGS="--stage-captures --require-discovery-coverage - --require-render-coverage --require-analysis-coverage - --evidence-dir ${{ runner.temp }}/compiler-differential-evidence/shipped" - name: Upload differing projects' targets, stderr and compiled-test stats if: failure() uses: actions/upload-artifact@v4 @@ -370,19 +364,13 @@ jobs: uv run --no-sync pytest tests/integration/src/sqlbuild/compiler/test_native_python_compat_oracle.py -n auto --dist loadfile - - name: Compare python with native-preview on fixtures, examples, the failure corpus and a few seeds + - name: Compare native with native-preview and the goldens on fixtures, examples, the failure corpus and a few seeds env: PYTHONUNBUFFERED: "1" run: >- uv run --no-sync python -m scripts.run_compiler_differential - --engines python native-preview + --engines native native-preview --goldens check --corpus fixtures examples failures seeds --seeds 4 --jobs 4 - - name: Compare python with shipped native on the failure corpus and a few seeds - env: - PYTHONUNBUFFERED: "1" - run: >- - uv run --no-sync python -m scripts.run_compiler_differential - --engines python native --corpus failures seeds --seeds 4 --jobs 4 native_discovery_windows: name: Native compiler on Windows @@ -420,14 +408,14 @@ jobs: tests/integration/src/sqlbuild/compiler/discovery/test_native_yaml_oracle.py tests/integration/src/sqlbuild/compiler/discovery/test_native_toml_oracle.py -n auto --dist loadfile - - name: Compile playground projects on the default and Python engines, and build + - name: Compile playground projects on the default and preview engines, and build run: | project="${RUNNER_TEMP}/waffle-shop" - oracle="${RUNNER_TEMP}/waffle-shop-python" + oracle="${RUNNER_TEMP}/waffle-shop-preview" uv run --no-sync sqb playground "${project}" cp -r "${project}" "${oracle}" uv run --no-sync sqb --project-dir "${project}" --no-color compile - SQLBUILD_COMPILER_ENGINE=python uv run --no-sync sqb --project-dir "${oracle}" --no-color compile + SQLBUILD_COMPILER_ENGINE=native-preview uv run --no-sync sqb --project-dir "${oracle}" --no-color compile diff -r "${oracle}/target/compiled" "${project}/target/compiled" if ! test -f "${project}/target/cache/compiler-native-v1/macro-calls.bin"; then echo "The default compile did not write the macro-call store; macro bridge debug log:" @@ -611,24 +599,18 @@ jobs: - kind: fresh max_ratio: "1.10" - kind: dense - label: native contracts vs python - base_engine: python - head_engine: native + label: contracts CPU vs base gate_phase: contracts_cpu_ms - max_ratio: "1.00" + max_ratio: "1.10" - kind: dense - label: native model analysis vs python - base_engine: python - head_engine: native + label: model analysis CPU vs base gate_phase: model_analysis_cpu_ms compile_args: --compile-arg=--select=* - max_ratio: "1.50" + max_ratio: "1.10" - kind: dense - label: native custom rules vs python - base_engine: python - head_engine: native + label: custom rules CPU vs base gate_phase: custom_rules_cpu_ms - max_ratio: "1.25" + max_ratio: "1.10" steps: - uses: actions/checkout@v4 - name: Check out the comparison base @@ -655,13 +637,11 @@ jobs: - name: Install base dependencies working-directory: base run: uv sync --locked --all-groups --all-extras - - name: Compare python and native phase CPU on head + - name: Compare base and head phase CPU if: matrix.gate_phase env: KIND: ${{ matrix.kind }} MAX_RATIO: ${{ matrix.max_ratio }} - BASE_ENGINE: ${{ matrix.base_engine }} - HEAD_ENGINE: ${{ matrix.head_engine }} GATE_PHASE: ${{ matrix.gate_phase }} COMPILE_ARGS: ${{ matrix.compile_args }} run: | @@ -670,10 +650,9 @@ jobs: --modes cold \ --max-ratio "${MAX_RATIO}" \ --models 3000 \ - --base-python .venv/bin/python \ + --base-root base \ + --base-python base/.venv/bin/python \ --head-python .venv/bin/python \ - --base-engine "${BASE_ENGINE}" \ - --head-engine "${HEAD_ENGINE}" \ --gate-phase "${GATE_PHASE}" \ ${COMPILE_ARGS:+"${COMPILE_ARGS}"} - name: Compare base and head cold, warm and one-model edit compiles diff --git a/Makefile b/Makefile index c084b7a977..b8dea2f83b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ SHELL := /bin/bash test-e2e-duckdb-integrations test-e2e-performance \ test-e2e-cold-compile-performance test-e2e-cache-compile-performance \ test-e2e-dense-compile-performance test-e2e-varied-cache-performance \ - compiler-differential compiler-differential-shipped compiler-differential-dense \ + compiler-differential compiler-differential-dense \ compiler-baselines format: @@ -291,7 +291,6 @@ test-e2e-varied-cache-performance: COMPILER_DIFFERENTIAL_JOBS ?= 4 COMPILER_DIFFERENTIAL_SEEDS ?= 12 -COMPILER_DIFFERENTIAL_SHIPPED_SEEDS ?= 12 COMPILER_DIFFERENTIAL_DENSE_MODELS ?= 3000 COMPILER_DIFFERENTIAL_ARGS ?= # Native-to-Python fallbacks and analysis deferrals must match the allow-list exactly, and every @@ -303,23 +302,13 @@ compiler-differential: --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SEEDS) \ $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) -# python vs native-preview above covers every native stage; this keeps the shipped `native` default -# (native discovery, rendering and analysis) and its goldens covered on the same corpus. -compiler-differential-shipped: - env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ - --engines python native --corpus fixtures examples seeds failures \ - --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SHIPPED_SEEDS) \ - $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) - -# Rewrite the goldens (from the python oracle) and the fallback allow-list from both CI runs. +# Rewrite the goldens (from the native oracle) and the fallback allow-list from the CI run. # Review the diff like code; see scripts/compiler_differential/README.md. COMPILER_DIFFERENTIAL_CI_ARGS := --stage-captures --require-discovery-coverage --require-render-coverage compiler-baselines: rm -rf tests/goldens/compiler $(MAKE) compiler-differential COMPILER_DIFFERENTIAL_ARGS="$(COMPILER_DIFFERENTIAL_CI_ARGS) \ --require-analysis-coverage --native-fallbacks update --goldens update" - $(MAKE) compiler-differential-shipped COMPILER_DIFFERENTIAL_ARGS="$(COMPILER_DIFFERENTIAL_CI_ARGS) \ - --require-analysis-coverage --native-fallbacks update --goldens update" compiler-differential-dense: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ diff --git a/crates/README.md b/crates/README.md index f990c2da28..161417bc63 100644 --- a/crates/README.md +++ b/crates/README.md @@ -58,19 +58,18 @@ stages run natively: | Engine | Runs | |---|---| -| `python` | The Python compiler only. It is the oracle every native stage must match byte for byte. | -| `native` | The default: native stages that passed their flip gate (`shipped` tier). | +| `native` | The default: discovery, rendering and analysis, and every native stage that passed its flip gate (`shipped` tier). | | `native-preview` | Opt-in: shipped stages plus stages still in development (`preview` tier). | -The shipped tier covers discovery, rendering and analysis: declaration files and scopes, model -config, reference extraction, the model loop, macro calls and the macro-call store, attachments, -the type system, model analysis and its `model-analyses.bin` cache, semantic checks, contracts, -lineage facts, SQL-test glue and project assembly. A failing -render raises its first error directly, as the Python stage would, and runs each macro call at -most once; a divergence the bridge detects is reported as a native stage mismatch. Because the -macro-call store is shipped, the +The Python compiler and its `python` engine were removed once discovery, rendering and analysis +shipped natively; setting `python` is an error that says so. Native stages still hand inputs they +cannot reproduce exactly back to Python helpers (the allow-list in +`scripts/compiler_differential/native_fallbacks.toml` lists every such deferral the corpus +reaches). A failing render raises its first error directly and runs each macro call at most +once; a divergence the bridge detects is reported as a native stage mismatch. Because the +macro-call store is always on, the [macro determinism contract](../website/src/content/docs/docs/concepts/macros.mdx) applies to -every default compile; `SQLBUILD_COMPILER_ENGINE=python` runs every macro call each time. +every compile. Each native stage declares its tier once, in `NATIVE_STAGE_TIERS` in `src/sqlbuild/compiler/frontier/constants.py`. A stage moves from `preview` to `shipped` by @@ -78,12 +77,10 @@ changing that line, after its flip gate passes: a byte-identical real project, a differential, and no measured slowdown on cold, edit and no-change compiles. Every engine keeps its own compiler, Rules and compile-reuse stores, so preview output is never reused by `native`. -`make compiler-differential` compares `python` with `native-preview` on the full per-PR corpus. -`make compiler-differential-shipped` compares `python` with `native` on the generated seeds and -the failure corpus, so the shipped default stays covered on its own; CI runs it with stage -captures and requires full discovery and render coverage. CI runs both, compares `python` with -`native` and `native-preview` on Python 3.13 and 3.14 as well, and on Windows checks that the -default compiles a playground project to the same files as `python`. +`make compiler-differential` compares `native` with `native-preview` on the full per-PR corpus +and checks both against the goldens in `tests/goldens/compiler`, with stage captures and full +discovery, render and analysis coverage required. CI also runs it on Python 3.13 and 3.14, and +on Windows checks that both engines compile a playground project to the same files. ## Working on the crates diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs index 2599db889d..4a35f05267 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs @@ -16,24 +16,6 @@ fn render_sql_test_comparisons_json(py: Python<'_>, request_json: &str) -> PyRes .map_err(value_error) } -#[pyfunction] -fn plan_and_render_sql_tests_json(py: Python<'_>, request_json: &str) -> PyResult { - py.compiler_detach(|| { - sqlbuild_analysis::compiler::main::sql_test_planning::plan_and_render_json(request_json) - }) - .map_err(value_error) -} - -#[pyfunction] -fn resolve_sql_test_chains_json(py: Python<'_>, request_json: &str) -> PyResult { - py.compiler_detach(|| { - sqlbuild_analysis::compiler::main::sql_test_chain_resolution::resolve_chains_json( - request_json, - ) - }) - .map_err(value_error) -} - #[pyfunction] fn render_sql_test_difference_sample_json(py: Python<'_>, request_json: &str) -> PyResult { py.compiler_detach(|| { @@ -91,8 +73,6 @@ pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(function_type_error, module)?)?; module.add_function(wrap_pyfunction!(extract_sql_scenario_json, module)?)?; module.add_function(wrap_pyfunction!(render_sql_test_comparisons_json, module)?)?; - module.add_function(wrap_pyfunction!(plan_and_render_sql_tests_json, module)?)?; - module.add_function(wrap_pyfunction!(resolve_sql_test_chains_json, module)?)?; module.add_function(wrap_pyfunction!( render_sql_test_difference_sample_json, module diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md index 2fd6b71f2b..f31d1410a9 100644 --- a/scripts/compiler_differential/README.md +++ b/scripts/compiler_differential/README.md @@ -1,13 +1,13 @@ # Compiler differential `make compiler-differential` compiles fixtures, examples, generated seeds and the failure corpus -under two compiler engines and fails on the first differing artifact. `make -compiler-differential-shipped` does the same for the shipped `native` default. Both also enforce -the shared gates below. +under the shipped `native` engine and `native-preview` and fails on the first differing artifact. +It also enforces the shared gates below. The goldens are the oracle for output that both engines +share: the Python compiler they once compared against has been removed. For ad hoc runs (one project, other seeds, other engines), call the script directly, for example -`uv run python -m scripts.run_compiler_differential --corpus --project --engines python -native`. The make targets fix the corpus the allow-list and goldens are recorded for. +`uv run python -m scripts.run_compiler_differential --corpus --project --engines native +native-preview`. The make targets fix the corpus the allow-list and goldens are recorded for. ## Shared gates for native ports @@ -23,39 +23,36 @@ and of the work each native stage answers itself. It covers three kinds of recor - analysis-stage deferrals: every `analysis-deferrals-*.jsonl` record (analysis session, lineage, semantic checks, contracts); - native answers, from `report_native_answer(stage=NativeStage., kind=...)` where a - stage's native path produced the result: files, models, scans or calls. Their site is + preview stage's native path produced the result: files, models, scans or calls. Their site is `.native`. Counting only fallbacks cannot tell a stage that answers natively from one that no longer runs -natively at all. The answer counts can: if a stage is switched to Python, its `.native` -entries vanish and the gate says so. +natively at all. The answer counts can: if a preview stage is switched off, its `.native` +entries vanish and the gate says so. A stage leaves `NativeStage`, and drops its answer counts, +once the Python engine it switched against is deleted. Every entry names an engine, stage, site and kind, with its exact count per corpus. Use `max_counts` instead of `counts` only for a count that is not stable, and give the reason in a comment and in the PR body. The records cost nothing unless `SQLBUILD_ANALYSIS_RECORD_DIR` is set, which only the harness -sets. `--native-fallbacks check` runs in both make targets and fails CI in both directions: +sets. `--native-fallbacks check` runs in the make target and fails CI in both directions: - an entry, engine or corpus count that is not on the list fails; - a listed entry whose count changed or that no longer occurs also fails, and the message says to update or remove it, so the fallback list only shrinks; - a vanished native answer fails as work that now runs in Python; - a listed fallback that vanished is refused outright, with "fallback disappeared but native answers did not appear; the stage may be switched off", unless the same engine, stage and - corpus report more `.native` answers than the list records. A stage with only fallback - entries (`sql_test_glue`, `project_assembly`) would otherwise look like a finished port when it - is switched off. + corpus report more `.native` answers than the list records. This applies only to stages + that are still `NativeStage` members, which the engine can switch off; a stage with only + fallback entries would otherwise look like a finished port when it is switched off. Shipped + stages that left `NativeStage` cannot be switched off, so their fallbacks may simply vanish. -The counts hold for the make targets' corpus (`--seeds 12`); other seed ranges are refused. +The counts hold for the make target's corpus (`--seeds 12`); other seed ranges are refused. -Two stages the list cannot watch: -- `type_system`: the compile corpus never reaches type normalization. Its callers are the planner - and executor and the analysis stages' Python fallbacks. `tests/integration/.../type_system/ - test_native_type_parity.py` proves instead that the public `normalize_type` asks native first - under `native` and `native-preview`. -- `discovery.native model_file_listings`: it is reported unconditionally inside the native model - file discovery, which has no fallback to Python, so this count cannot detect anything. It stays - only as a record that native discovery ran. +The compile corpus never reaches type normalization (its callers are the planner, the executor +and the analysis stages' Python fallbacks), so `tests/integration/.../type_system/ +test_native_type_parity.py` covers that its public `normalize_type` asks native first. **How a PR updates the list and the goldens** @@ -65,13 +62,13 @@ One command rewrites both from the current tree: make compiler-baselines ``` -It deletes `tests/goldens/compiler`, then runs the two CI differentials with `--native-fallbacks +It deletes `tests/goldens/compiler`, then runs the CI differential with `--native-fallbacks update --goldens update`. Commit the resulting `native_fallbacks.toml` and `tests/goldens/` diff in the same PR as the change that caused it. - A port deletes the Python code it replaces in the same PR. Its fallback entries then disappear - from the list, and that removal is the port's test that the fallbacks are gone. Its answer - counts usually rise. When the last entry for a `NativeFallbackSite` goes, delete the member + from the list, and that removal is the port's test that the fallbacks are gone. A preview + stage's answer counts usually rise. When the last entry for a `NativeFallbackSite` goes, delete the member and its `report_native_fallback` call too. - If the corpus does not reach a site yet, first add a failure case or seed feature that does. - A PR that merges after a list or golden change on main reruns `make compiler-baselines` after @@ -112,9 +109,9 @@ masked. Goldens hold outputs only, not stage captures: a seed's captures are about 690 KB, too large to review. Cache facts are covered by the incremental-versus-no-cache tests instead. -- `--goldens check` (in both make targets) compares every engine with the goldens, so an +- `--goldens check` (in the make target) compares every engine with the goldens, so an output-neutral port needs no new parity suite. -- `make compiler-baselines` rewrites them from the python oracle. Run it only when output +- `make compiler-baselines` rewrites them from the `native` oracle. Run it only when output changes on purpose, and review the diff like code. - `tests/goldens/compiler/seed_range.toml` records the seed range the goldens were written for. Every seed in that range, and every fixture, example and failure case, must have a golden. @@ -123,15 +120,14 @@ review. Cache facts are covered by the incremental-versus-no-cache tests instead - Dialect variants (`-postgres`, `-snowflake`) are generated for the first seed of a run only, so a golden is required only for the variants of the recorded `seed_start`. An ad hoc `--seed-start N` run checks its base seeds in the range and skips its variants. -- Once the Python stages are deleted, goldens are rewritten from `native`. ### All-engine user-facing errors `engine_error_cases()` in `_helpers/corpus/failure_cases.py` lists user-facing errors with their -exact text. Each case is part of the failure corpus, so it runs in both differentials and has a +exact text. Each case is part of the failure corpus, so it runs in the differential and has a golden. It also runs in -`tests/e2e/scripts/compiler_differential/test_engine_error_cases.py` on the `python`, `native` -and `native-preview` engines, which must all report its `expected_code` and `expected_message`. +`tests/e2e/scripts/compiler_differential/test_engine_error_cases.py` on the `native` and +`native-preview` engines, which must both report its `expected_code` and `expected_message`. A port that adds or changes a user-facing error adds one `failure_case(...)` to it: diff --git a/scripts/compiler_differential/_helpers/running/native_fallbacks.py b/scripts/compiler_differential/_helpers/running/native_fallbacks.py index b9d50c366f..625c5ed66f 100644 --- a/scripts/compiler_differential/_helpers/running/native_fallbacks.py +++ b/scripts/compiler_differential/_helpers/running/native_fallbacks.py @@ -25,6 +25,7 @@ RecordedRun, ) from scripts.compiler_differential.types import FallbackKey +from sqlbuild.compiler.frontier.types import NativeStage def native_fallback_gate( @@ -118,6 +119,7 @@ def fallback_problems( allowed_answers: Counter[tuple[str, str, str]] = _answer_totals( {**allowed.counts, **allowed.max_counts} ) + switchable_stages: frozenset[str] = frozenset(stage.value for stage in NativeStage) problems: list[str] = [] for key in sorted({*allowed.counts, *allowed.max_counts, *observed}): if key[0] not in run.engines: @@ -134,6 +136,7 @@ def fallback_problems( bound=None if bound is None else bound.get(corpus, 0), answers_rose=observed_answers[(key[0], key[1], corpus)] > allowed_answers[(key[0], key[1], corpus)], + switchable=key[1] in switchable_stages, ) if problem is not None: problems.append(problem) @@ -196,6 +199,7 @@ def _corpus_problem( exact: int | None, bound: int | None, answers_rose: bool, + switchable: bool, ) -> str | None: if answer and exact is None and bound is None: return f"{label}: native answered {actual}, not on the allow-list; record it" @@ -206,7 +210,7 @@ def _corpus_problem( ) if exact is None and bound is None: return f"{label}: {actual} not on the allow-list; port it or list it with a reason" - if actual == 0 and not answers_rose: + if actual == 0 and switchable and not answers_rose: return ( f"{label}: fallback disappeared but native answers did not appear; the stage may be " "switched off. A port that removes a fallback must report native answers for the " diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index e78925f6af..1581a6a3ef 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -31,7 +31,6 @@ "recovery.py": "semantic_checks", "metadata_validation.py": "semantic_checks", "contracts/columns.py": "contracts", - "contracts/promotion.py": "contracts", } NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE: str = "analysis" NATIVE_FALLBACK_LIST_HEADER: str = ( @@ -62,8 +61,8 @@ GOLDEN_WORK_MASK: str = "" GOLDEN_VERSION_MASK: str = "" GOLDEN_MISSING_HINT: str = "no golden; record it with `make compiler-baselines`" -ENGINE_NAMES: tuple[str, ...] = ("python", "native", "native-preview") -DEFAULT_ENGINES: tuple[str, str] = ("python", "native-preview") +ENGINE_NAMES: tuple[str, ...] = ("native", "native-preview") +DEFAULT_ENGINES: tuple[str, str] = ("native", "native-preview") SQB_ENTRY: str = "import sys; from sqlbuild.cli.entry.main.entry import main; sys.exit(main())" EXCLUDED_ENVIRONMENT_KEYS: frozenset[str] = frozenset( {"VIRTUAL_ENV", ENGINE_ENV_VAR, STAGE_CAPTURE_ENV_VAR, ANALYSIS_RECORD_ENV_VAR} diff --git a/scripts/compiler_differential/main/differential.py b/scripts/compiler_differential/main/differential.py index 2ac8f10ce0..1b3fee8a07 100644 --- a/scripts/compiler_differential/main/differential.py +++ b/scripts/compiler_differential/main/differential.py @@ -153,8 +153,8 @@ def _parse_args(argv: list[str] | None) -> argparse.Namespace: default=list(DEFAULT_ENGINES), metavar="ENGINE", help=( - "oracle and candidate engines (default: python native-preview, every native stage; " - "pass python native to cover only the shipped native stages)" + "oracle and candidate engines (default: native native-preview, the shipped default " + "against every preview stage)" ), ) parser.add_argument("--jobs", type=int, default=os.cpu_count() or 1) diff --git a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py index 98c8f6844c..15409a4ceb 100644 --- a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py +++ b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py @@ -42,10 +42,8 @@ TEXT_TYPE_NAME, UNBOUNDED_TEXT_TYPE_NAMES, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot from sqlbuild.diagnostics.main.log_debug_event import log_debug_event @@ -56,14 +54,12 @@ def normalize_type(*, type_sql: str, dialect: TypeDialect | str | None) -> NormalizedType: """Normalize one warehouse type string into a semantic comparison shape.""" - if native_stage_enabled(NativeStage.TYPE_SYSTEM): - native_normalized: NormalizedType | None = normalize_native_type( - type_sql=type_sql, dialect=dialect - ) - if native_normalized is not None: - report_native_answer(stage=NativeStage.TYPE_SYSTEM, kind="type_normalizations") - return native_normalized - report_native_fallback(site=NativeFallbackSite.TYPE_NORMALIZATION) + native_normalized: NormalizedType | None = normalize_native_type( + type_sql=type_sql, dialect=dialect + ) + if native_normalized is not None: + return native_normalized + report_native_fallback(site=NativeFallbackSite.TYPE_NORMALIZATION) polyglot_normalized: NormalizedType | None = _normalize_with_polyglot( type_sql=type_sql, dialect=dialect, diff --git a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py index c3aba5c289..f8996b2463 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py @@ -47,13 +47,7 @@ DiagnosticSeverity, FunctionLanguage, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.planner.exceptions import PlannerInputError -from sqlbuild.compiler.planner.main.execution.sql_test_artifacts import ( - plan_and_render_sql_test_artifacts, -) from sqlbuild.compiler.planner.main.execution.sql_test_model_chain import ( sql_test_model_chain_names_by_key, ) @@ -576,20 +570,12 @@ def _plan_static_test_artifacts( with OperationLifecycle( operation_kind="project", operation_name="sql_test_planning" ) as lifecycle: - native_planning: bool = native_stage_enabled(NativeStage.SQL_TEST_GLUE) - plan_artifacts: Callable[..., tuple[NativeSqlTestArtifact, ...]] = ( - plan_native_sql_test_artifacts - if native_planning - else plan_and_render_sql_test_artifacts - ) - native_artifacts: tuple[NativeSqlTestArtifact, ...] = plan_artifacts( + native_artifacts: tuple[NativeSqlTestArtifact, ...] = plan_native_sql_test_artifacts( project=project, tests=tests, adapter=adapter, sql_analysis_enabled=project.settings.sql_analysis, ) - if native_planning: - report_native_answer(stage=NativeStage.SQL_TEST_GLUE, kind="sql_test_plans") lifecycle.completed(metadata={"item_count": len(native_artifacts)}) return native_artifacts diff --git a/src/sqlbuild/cli/commands/_helpers/entry/parser.py b/src/sqlbuild/cli/commands/_helpers/entry/parser.py index 17e9c79555..d931bfe089 100644 --- a/src/sqlbuild/cli/commands/_helpers/entry/parser.py +++ b/src/sqlbuild/cli/commands/_helpers/entry/parser.py @@ -29,7 +29,7 @@ ) from sqlbuild.cli.commands.types import CliCommand, CompileLineageMode from sqlbuild.compiler.contract_adoption.types import ContractAction -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_OPTION, COMPILER_ENGINE_VALUES +from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_OPTION from sqlbuild.compiler.lineage.types import ColumnLineageMode from sqlbuild.compiler.scopes.types import DeclarationKind @@ -102,7 +102,6 @@ def _add_global_args(*, parser: argparse.ArgumentParser, suppress_defaults: bool parser.add_argument( COMPILER_ENGINE_OPTION, dest="compiler_engine", - choices=COMPILER_ENGINE_VALUES, default=argparse.SUPPRESS if suppress_defaults else None, help=argparse.SUPPRESS, ) diff --git a/src/sqlbuild/cli/commands/_helpers/entry/parsing.py b/src/sqlbuild/cli/commands/_helpers/entry/parsing.py index 70575c1dce..4351baf7d8 100644 --- a/src/sqlbuild/cli/commands/_helpers/entry/parsing.py +++ b/src/sqlbuild/cli/commands/_helpers/entry/parsing.py @@ -25,7 +25,9 @@ SelectorFileSummary, SelectorInputs, ) +from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_OPTION from sqlbuild.compiler.frontier.exceptions import CompilerEngineError +from sqlbuild.compiler.frontier.main.parse_compiler_engine import parse_compiler_engine from sqlbuild.compiler.frontier.main.resolve_compiler_engine import resolve_compiler_engine from sqlbuild.compiler.scopes.types import DeclarationKind from sqlbuild.integrations.dbt.types import DbtInteropCommand @@ -117,11 +119,16 @@ def parse_cli_invocation( if unknown_args: parser.error(f"unrecognized arguments: {' '.join(unknown_args)}") args.verbose = args.verbose or args.debug - if args.compiler_engine is None: - try: - _ = resolve_compiler_engine() - except CompilerEngineError as error: - parser.error(str(error)) + try: + _ = ( + resolve_compiler_engine() + if args.compiler_engine is None + else parse_compiler_engine( + raw_value=args.compiler_engine, source=COMPILER_ENGINE_OPTION + ) + ) + except CompilerEngineError as error: + parser.error(str(error)) if ( args.debug and args.command == CliCommand.DBT diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py index 1013e3e9fe..4f063906f9 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py @@ -69,8 +69,6 @@ DiscoveredAuditFile, DiscoveredProjectInputs, ) -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.scopes.models import ( DeclarationIdentity, @@ -653,7 +651,6 @@ def build_attached_audit_input( explicit_arguments=audit_instance.arguments, policies=_native_audit_policies(audit_instance=audit_instance, context=context), ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="audit_renderings") if native_rendering.render_error is not None: raise CompileInputError(native_rendering.render_error) rendered_sql_body: str = native_rendering.sql_body diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py index c3f33a7fe7..fd6285d374 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py @@ -127,9 +127,6 @@ PythonHookEntry, SqlHookEntry, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.model_loop.main._scan_native_declaration_references import ( scan_native_declaration_references, ) @@ -524,9 +521,6 @@ def _build_model_inputs( for model_file in render_files if interpolations[model_file.file_path].error is None ) - report_native_answer( - stage=NativeStage.MODEL_LOOP, kind="variable_substitutions", units=len(prepared_files) - ) loop: _ModelInputLoop = _ModelInputLoop( discovered_inputs=discovered_inputs, context=context, @@ -666,7 +660,6 @@ def _build_model_input( value_renderer=context.value_renderer, collection_rendering=context.collection_rendering, ) - report_native_answer(stage=NativeStage.MODEL_LOOP, kind="declaration_expansions") declaration_expanded_sql: str = declaration_expansion.sql try: macro_expansion: MacroExpansionResult = expand_sql_macros_result( @@ -1103,34 +1096,9 @@ def build_seed_inputs(discovered_inputs: DiscoveredProjectInputs) -> tuple[Compi for seed_entry in schema_file.seed_entries: seed_declarations.append((seed_entry, schema_file)) - if native_stage_enabled(NativeStage.ATTACHMENTS): - return _native_seed_inputs( - seed_declarations=seed_declarations, seed_files=discovered_inputs.seed_files - ) - seed_files_by_name: dict[str, DiscoveredSeedFile] = { - seed_file.file_path.stem: seed_file for seed_file in discovered_inputs.seed_files - } - - seed_inputs: list[CompileSeedInput] = [] - seed_entry: SchemaSeedEntry - seed_schema_file: DiscoveredSchemaFile - for seed_entry, seed_schema_file in seed_declarations: - seed_file: DiscoveredSeedFile | None = seed_files_by_name.get(seed_entry.name) - if seed_file is None: - raise CompileInputError( - f"Seed declaration '{seed_entry.name}' in {seed_schema_file.relative_path} " - "has no matching CSV file under seeds/" - ) - - seed_inputs.append( - CompileSeedInput( - seed_file=seed_file, - schema_entry=seed_entry, - schema_file=seed_schema_file, - ) - ) - - return tuple(seed_inputs) + return _native_seed_inputs( + seed_declarations=seed_declarations, seed_files=discovered_inputs.seed_files + ) def _native_seed_inputs( @@ -1150,7 +1118,6 @@ def _native_seed_inputs( f"Seed declaration '{seed_entry.name}' in {seed_schema_file.relative_path} " "has no matching CSV file under seeds/", ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="seed_pairs", units=len(pairs)) return tuple( CompileSeedInput( seed_file=seed_files[file_index], diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py index 8d718c5ef0..6ae2824615 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py @@ -24,8 +24,6 @@ LoadedMacro, ) from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.scopes.classes.native_scope_index import NativeScopeIndex from sqlbuild.compiler.scopes.exceptions import ScopeValidationError from sqlbuild.compiler.scopes.main._open_native_scope_index import open_native_scope_index @@ -90,7 +88,6 @@ def _build_native_declaration_scope( native.grant(facts) index: ScopeIndex = native.index_with_relationships() lookup: ScopeLookup = native.lookup(index=index) - report_native_answer(stage=NativeStage.DECLARATION_SCOPES, kind="scope_indexes") return DeclarationScopeBuild( loaded_macros=loaded_macros, index=index, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py index 2d6365c585..c6d447f607 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py @@ -72,8 +72,6 @@ DiscoveredPythonFunctionFile, DiscoveredSqlFunctionFile, ) -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.scopes.models import ResourceIdentity from sqlbuild.compiler.scopes.types import ResourceKind from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax @@ -317,7 +315,6 @@ def _sql_function_header( python=False, relative_path=function_file.relative_path, ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="function_headers") _raise_header_failure(header=native_header, stage=HEADER_START_STAGE) arguments: tuple[FunctionArgument, ...] = _expanded_native_arguments( header=native_header, @@ -435,7 +432,6 @@ def _build_python_function_input( python=True, relative_path=python_function_file.relative_path, ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="function_headers") _raise_header_failure(header=native_header, stage=HEADER_START_STAGE) arguments: tuple[FunctionArgument, ...] = _expanded_native_arguments( header=native_header, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py index 409ae03a68..be34fdd96c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py @@ -51,8 +51,6 @@ PythonHookEntry, SqlHookEntry, ) -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.model_config.main._native_config_error import native_config_error from sqlbuild.compiler.model_config.main._parse_native_header_metadata import ( parse_native_header_metadata, @@ -238,7 +236,6 @@ def build_model_header_schema_entry( column_locations=column_locations or {}, file_path=file_path, ) - report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="header_metadata_parses") if native.columns_error is not None: raise native_config_error(error=native.columns_error) local_columns: tuple[SchemaColumn, ...] = native.columns @@ -706,7 +703,6 @@ def build_native_model_config( built: _BuiltConfig | _native.NativeConfigError = session.builder.build( model_file.header_values, matched_path_default, model_file.file_path.stem ) - report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="config_builds") if isinstance(built, _native.NativeConfigError): raise native_config_error(error=built) values, header_keys, namespace, overrides, reads = built @@ -800,7 +796,6 @@ def validate_model_config( request=request, rejected=[error is not None for error in external_errors], ) - report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="validations") _raise_validation_outcome( outcome=outcome, external_errors=external_errors, values=request.config.values ) diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py index 6c9669aff8..b248915873 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py @@ -33,8 +33,6 @@ ) from sqlbuild.compiler.compile.types import SqlTestMode from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs, DiscoveredSqlTestFile -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.scopes.main._native_expected_model_names import ( native_expected_model_names, ) @@ -151,11 +149,6 @@ def extract_scope_relationship_facts( scenario=True, syntax=sql_lexical_syntax, ) - report_native_answer( - stage=NativeStage.DECLARATION_SCOPES, - kind="relationship_scans", - units=len(scenario_names) + len(macro_blocks), - ) for scenario, scanned in zip(discovered_inputs.scenario_files, scenario_names, strict=True): if isinstance(scanned, str): faults.append(ScopeRelationshipFault(scenario.relative_path, scanned)) @@ -290,9 +283,6 @@ def _scanned_expected_names( scenario=False, syntax=syntax, ) - report_native_answer( - stage=NativeStage.DECLARATION_SCOPES, kind="relationship_scans", units=len(scanned) - ) return { position: CompileInputError(names) if isinstance(names, str) else names for position, names in zip(pending, scanned, strict=True) diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/attachment/sql_tests.py index c26f1fb311..512861d990 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/sql_tests.py @@ -84,8 +84,6 @@ DiscoveredSqlTestFile, EnumDeclaration, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.profiling.main.record import record_compile_timing from sqlbuild.compiler.references.types import ExternalSqlReferenceResolver, SqlReferenceKind from sqlbuild.compiler.scopes.models import ( @@ -327,16 +325,12 @@ def build_test_inputs( test_block=failed.test_block, sql=failed.sql_body, ) from None - target_catalog: _native.SqlTestTargetCatalog | None = ( - native_test_target_catalog( - models=known_model_names, - sources=known_source_names, - seeds=known_seed_names, - table_functions=known_table_function_names, - macros=loaded_macros, - ) - if native_stage_enabled(NativeStage.ATTACHMENTS) - else None + target_catalog: _native.SqlTestTargetCatalog = native_test_target_catalog( + models=known_model_names, + sources=known_source_names, + seeds=known_seed_names, + table_functions=known_table_function_names, + macros=loaded_macros, ) for test, test_ctes in zip(expanded_tests, test_ctes_batch, strict=True): assertion_target_model_names: tuple[str, ...] = ( @@ -700,10 +694,8 @@ def build_scenario_inputs( ) ) scenario_inputs: list[CompileSqlScenarioInput] = [] - source_catalog: _native.SqlTestTargetCatalog | None = ( - native_test_target_catalog(sources=known_source_names) - if native_stage_enabled(NativeStage.ATTACHMENTS) - else None + source_catalog: _native.SqlTestTargetCatalog = native_test_target_catalog( + sources=known_source_names ) scenario_file: DiscoveredSqlScenarioFile for scenario_file in discovered_inputs.scenario_files: @@ -792,49 +784,19 @@ def _validate_scenario_source_references( syntax: SqlLexicalSyntax, target_catalog: _native.SqlTestTargetCatalog | None = None, ) -> None: - if native_stage_enabled(NativeStage.ATTACHMENTS): - cte_sources: list[tuple[str, bool, list[str]]] - extraction_error: CompileInputError | None - cte_sources, extraction_error = _scenario_cte_sources( - scenario_ctes=scenario_ctes, scenario_file=scenario_file, syntax=syntax - ) - error: str | None = ( - target_catalog or native_test_target_catalog(sources=known_source_names) - ).scenario_source_error(str(scenario_file.relative_path), cte_sources) - if error is not None: - raise CompileInputError(error) - if extraction_error is not None: - raise extraction_error - return - cte: CompileSqlScenarioCte - for cte in (*scenario_ctes.expected_ctes, *scenario_ctes.assertion_ctes): - references: tuple[CompileSqlReference, ...] = extract_sql_references( - sql=cte.sql_body, syntax=syntax, origin=_sql_file_reference_origin(scenario_file) - ) - reference: CompileSqlReference - for reference in references: - if reference.ref_kind != SqlReferenceKind.SOURCE: - continue - raise CompileInputError( - f"SQL scenario file {scenario_file.relative_path} CTE '{cte.name}' must not " - f"reference project source '{reference.ref_name}' with " - f"{SqlReferenceKind.SOURCE.placeholder_call()}; source-backed scenario data " - "is only allowed in helper and fixture CTEs" - ) - - for cte in scenario_ctes.authored_ctes: - references = extract_sql_references( - sql=cte.sql_body, syntax=syntax, origin=_sql_file_reference_origin(scenario_file) - ) - for reference in references: - if reference.ref_kind != SqlReferenceKind.SOURCE: - continue - if reference.ref_name in known_source_names: - continue - raise CompileInputError( - f"SQL scenario file {scenario_file.relative_path} references unknown source " - f"'{reference.ref_name}'" - ) + cte_sources: list[tuple[str, bool, list[str]]] + extraction_error: CompileInputError | None + cte_sources, extraction_error = _scenario_cte_sources( + scenario_ctes=scenario_ctes, scenario_file=scenario_file, syntax=syntax + ) + error: str | None = ( + target_catalog or native_test_target_catalog(sources=known_source_names) + ).scenario_source_error(str(scenario_file.relative_path), cte_sources) + if error is not None: + raise CompileInputError(error) + if extraction_error is not None: + raise extraction_error + return def _scenario_cte_sources( @@ -889,73 +851,25 @@ def validate_test_ctes( return model_payload: CompileModelSqlTestCtes = test_ctes.payload - if native_stage_enabled(NativeStage.ATTACHMENTS): - catalog: _native.SqlTestTargetCatalog = target_catalog or native_test_target_catalog( - models=known_model_names, - sources=known_source_names, - seeds=known_seed_names, - table_functions=known_table_function_names, - macros=loaded_macros, - ) - error: str | None = catalog.unknown_test_target( - str(test_file.relative_path), - ( - [*model_payload.mock_model_names], - [*model_payload.mock_source_names], - [*model_payload.mock_seed_names], - [*model_payload.mock_table_function_names], - [*model_payload.macro_mocks], - [*model_payload.expected_model_names], - [*assertion_target_model_names], - ), - ) - if error is not None: - raise CompileInputError(error) - return - - mock_model_name: str - for mock_model_name in model_payload.mock_model_names: - if mock_model_name not in known_model_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} mocks unknown model '{mock_model_name}'" - ) - mock_source_name: str - for mock_source_name in model_payload.mock_source_names: - if mock_source_name not in known_source_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} mocks unknown source '{mock_source_name}'" - ) - mock_seed_name: str - for mock_seed_name in model_payload.mock_seed_names: - if mock_seed_name not in known_seed_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} mocks unknown seed '{mock_seed_name}'" - ) - mock_table_function_name: str - for mock_table_function_name in model_payload.mock_table_function_names: - if mock_table_function_name not in known_table_function_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} mocks unknown table function " - f"'{mock_table_function_name}'" - ) - macro_mock_name: str - for macro_mock_name in model_payload.macro_mocks: - if macro_mock_name not in loaded_macros: - raise CompileInputError( - f"SQL test file {test_file.relative_path} mocks unknown macro '{macro_mock_name}'" - ) - expected_model_name: str - for expected_model_name in model_payload.expected_model_names: - if expected_model_name not in known_model_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} expects unknown model " - f"'{expected_model_name}'" - ) - - assertion_target_name: str - for assertion_target_name in assertion_target_model_names: - if assertion_target_name not in known_model_names: - raise CompileInputError( - f"SQL test file {test_file.relative_path} assertion references unknown model " - f"'{assertion_target_name}'" - ) + catalog: _native.SqlTestTargetCatalog = target_catalog or native_test_target_catalog( + models=known_model_names, + sources=known_source_names, + seeds=known_seed_names, + table_functions=known_table_function_names, + macros=loaded_macros, + ) + error: str | None = catalog.unknown_test_target( + str(test_file.relative_path), + ( + [*model_payload.mock_model_names], + [*model_payload.mock_source_names], + [*model_payload.mock_seed_names], + [*model_payload.mock_table_function_names], + [*model_payload.macro_mocks], + [*model_payload.expected_model_names], + [*assertion_target_model_names], + ), + ) + if error is not None: + raise CompileInputError(error) + return diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/target.py b/src/sqlbuild/compiler/compile/_helpers/attachment/target.py index 384a535141..4bbb400d85 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/target.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/target.py @@ -19,7 +19,7 @@ from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs from sqlbuild.compiler.frontier.constants import ( COMPILER_CACHE_DIRECTORY_NAME, - ENGINE_CACHE_NAMESPACE_SUFFIXES, + RETIRED_CACHE_NAMESPACE_SUFFIXES, ) from sqlbuild.compiler.frontier.main.compiler_cache_directory import compiler_cache_directory from sqlbuild.spec.contracts.main.resolve_target_config import resolve_target_config @@ -77,7 +77,7 @@ def compile_cache_root( def _remove_retired_fact_caches(*, root: Path) -> None: """Delete the per-file fact caches older releases kept under every engine's compiler root.""" - for suffix in ENGINE_CACHE_NAMESPACE_SUFFIXES.values(): + for suffix in RETIRED_CACHE_NAMESPACE_SUFFIXES: retired: Path = ( root.parent / f"{COMPILER_CACHE_DIRECTORY_NAME}{suffix}" diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py index 6f5c027477..6f83b35163 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py @@ -28,9 +28,6 @@ CompiledProject, CompilerDiagnostic, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.semantic_checks.main._complete_native_semantic_diagnostics import ( complete_native_semantic_diagnostics, ) @@ -49,17 +46,15 @@ def complete_semantic_diagnostics( native_session: Any | None = None, ) -> CompiledProject: """Recover type facts before metadata checks, then explain retained root diagnostics.""" - if native_stage_enabled(NativeStage.SEMANTIC_CHECKS): - native_project: CompiledProject | None = complete_native_semantic_diagnostics( - project=project, - profile=profile, - binding_results=binding_results, - resource_sql_analysis=resource_sql_analysis, - session=native_session, - ) - if native_project is not None: - report_native_answer(stage=NativeStage.SEMANTIC_CHECKS, kind="semantic_completions") - return native_project + native_project: CompiledProject | None = complete_native_semantic_diagnostics( + project=project, + profile=profile, + binding_results=binding_results, + resource_sql_analysis=resource_sql_analysis, + session=native_session, + ) + if native_project is not None: + return native_project project = recover_output_types(project=project, binding_results=binding_results) project = replace( project, diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py index 6db1cd78b5..58176d71a6 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py @@ -32,8 +32,6 @@ SQL_ANALYSIS_CONFIG_KEY, ) from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.project_assembly.main._check_native_sql_syntax import ( check_native_sql_syntax, ) @@ -80,16 +78,15 @@ def rejected_sql_analysis_opt_out(request: SqlAnalysisOptOutRequest) -> SourceLo def _parses(request: SqlAnalysisOptOutRequest) -> bool: """Whether the model query and every hook pass Python's syntax validation.""" - if native_stage_enabled(NativeStage.PROJECT_ASSEMBLY): - statements: list[str] = [request.query_sql] - for hook_name in _HOOK_KEYS: - statements.extend(hook_sql_statements(request.config.values.get(hook_name))) - native: bool | None = check_native_sql_syntax( - checks=tuple((statement, request.placeholders) for statement in statements), - dialect=None, - ) - if native is not None: - return native + statements: list[str] = [request.query_sql] + for hook_name in _HOOK_KEYS: + statements.extend(hook_sql_statements(request.config.values.get(hook_name))) + native: bool | None = check_native_sql_syntax( + checks=tuple((statement, request.placeholders) for statement in statements), + dialect=None, + ) + if native is not None: + return native model_name: str = request.model_file.file_path.stem try: validate_sql_syntax( diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py index 8e8c3238f2..1901cba38e 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py @@ -6,8 +6,6 @@ from sqlbuild.compiler.compile._helpers.attachment.target import build_compile_target_context from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.main._claim_first_compile import claim_first_compile from sqlbuild.compiler.macro_bridge.main.active_macro_bridge import active_macro_bridge @@ -38,7 +36,6 @@ def target_context_with_macro_call_store( and bridge is not None and compile_cache_dir is not None and project_dir is not None - and native_stage_enabled(NativeStage.MACRO_CALL_STORE) ): bridge.attach_store( cache_dir=compile_cache_dir, diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index dce51b5f95..739dece3ea 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -21,9 +21,8 @@ MacroExpansionState, ) from sqlbuild.compiler.frontier.exceptions import NativeStageMismatchError -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( ARGUMENT_REFERENCE_EVENT, @@ -87,10 +86,6 @@ def expand_bridged_sql_macros( ) for site, call_class in zip(sites, call_classes, strict=True) ] - mocked: int = sum(bool(state.macro_overrides.keys() & set(site.tree_names)) for site in sites) - report_native_answer( - stage=NativeStage.MACRO_CALLS, kind="bridged_calls", units=len(sites) - mocked - ) return bridge.splice(sql=sql, sites=sites, outputs=outputs) diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 8d44c731cb..ecbe869bd2 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -30,10 +30,8 @@ DynamicColumnContractProof, ModelSqlAnalysis, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.project_assembly.main._assemble_native_project_resources import ( assemble_native_project_resources, @@ -60,8 +58,6 @@ def project_facts_by_engine( ) -> NativeProjectFacts | None: """Return the native project facts, or None where Python must derive all of them.""" - if not native_stage_enabled(NativeStage.PROJECT_ASSEMBLY): - return None resources: NativeProjectResources | None = assemble_native_project_resources( inputs=inputs, dialect=dialect, @@ -75,14 +71,10 @@ def project_facts_by_engine( ), ), ) - if resources is not None: - report_native_answer(stage=NativeStage.PROJECT_ASSEMBLY, kind="project_assemblies") - else: + if resources is None: report_native_fallback(site=NativeFallbackSite.PROJECT_ASSEMBLY) - pivots: dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]] = ( - standalone_pivot_models(model_inputs=inputs.model_inputs, analyses=analyses) - if native_stage_enabled(NativeStage.MODEL_ANALYSIS) - else {} + pivots: dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]] = standalone_pivot_models( + model_inputs=inputs.model_inputs, analyses=analyses ) proofs: dict[int, DynamicColumnContractProof | None] = dict( zip( @@ -101,10 +93,6 @@ def project_facts_by_engine( strict=True, ) ) - proven: int = len([proof for proof in proofs.values() if proof is not None]) - report_native_answer( - stage=NativeStage.MODEL_ANALYSIS, kind="dynamic_contract_proofs", units=proven - ) return NativeProjectFacts( models=tuple( NativeModelFacts( @@ -122,15 +110,11 @@ def expression_source_shapes_by_engine( ) -> tuple[dict[str, str] | None, ...]: """Infer one shape per expression source natively, or with Python where native defers.""" - if native_stage_enabled(NativeStage.MODEL_ANALYSIS): - native_shapes: tuple[dict[str, str] | None, ...] | None = ( - infer_native_expression_source_shapes(expressions=expressions, profile=profile) - ) - if native_shapes is not None: - report_native_answer( - stage=NativeStage.MODEL_ANALYSIS, kind="expression_shapes", units=len(native_shapes) - ) - return native_shapes + native_shapes: tuple[dict[str, str] | None, ...] | None = infer_native_expression_source_shapes( + expressions=expressions, profile=profile + ) + if native_shapes is not None: + return native_shapes return get_expression_source_shapes(expressions=expressions, profile=profile) @@ -141,17 +125,13 @@ def analyze_model_sql_by_engine( ) -> tuple[dict[str, ModelSqlAnalysis], Any | None]: """Analyze models natively, or with Python; also return the finished native session.""" - if native_stage_enabled(NativeStage.MODEL_ANALYSIS): - native: NativeModelAnalyses | None = analyze_native_model_sql( - request=NativeModelAnalysisRequest( - **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table - ) + native: NativeModelAnalyses | None = analyze_native_model_sql( + request=NativeModelAnalysisRequest( + **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table ) - if native is not None: - report_native_answer( - stage=NativeStage.MODEL_ANALYSIS, kind="model_analyses", units=len(native.analyses) - ) - return native.analyses, native.session + ) + if native is not None: + return native.analyses, native.session return python_analysis(), None diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py index 06f93455d9..a154f8afce 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py @@ -1,4 +1,4 @@ -"""Hand compiled SQL test assembly to its native stage when enabled.""" +"""Assemble compiled SQL tests natively, with Python answering native deferrals.""" from __future__ import annotations @@ -10,10 +10,8 @@ CompileProjectInputs, CompileSqlTestInput, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.sql_test_glue.main._assemble_native_sql_tests import ( assemble_native_sql_tests, ) @@ -27,8 +25,6 @@ def assemble_sql_tests_by_engine( ) -> tuple[CompiledSqlTest, ...]: """Assemble every SQL test natively, or each test in Python where native defers.""" - if not native_stage_enabled(NativeStage.SQL_TEST_GLUE): - return tuple(assemble_python_test(test_input) for test_input in inputs.test_inputs) native: tuple[NativeSqlTestAssembly | None, ...] = assemble_native_sql_tests(inputs=inputs) tests: list[CompiledSqlTest] = [] for test_input, assembly in zip(inputs.test_inputs, native, strict=True): @@ -38,6 +34,5 @@ def assemble_sql_tests_by_engine( continue for key, diagnostic in assembly.diagnostics: report_compile_diagnostic(key=key, diagnostic=diagnostic) - report_native_answer(stage=NativeStage.SQL_TEST_GLUE, kind="sql_test_assemblies") tests.append(assembly.test) return tuple(tests) diff --git a/src/sqlbuild/compiler/compile/_helpers/refs/references.py b/src/sqlbuild/compiler/compile/_helpers/refs/references.py index 31da788a7e..ce170e1ba8 100644 --- a/src/sqlbuild/compiler/compile/_helpers/refs/references.py +++ b/src/sqlbuild/compiler/compile/_helpers/refs/references.py @@ -26,8 +26,6 @@ DiagnosticSeverity, SqlReferenceScanFailure, ) -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax from sqlbuild.spec.contracts.models import SourceLocation @@ -61,7 +59,6 @@ def _reference_scan_outcome( outcome: SqlReferenceScan | SqlReferenceScanFailure = extract_native_sql_references( sql=sql, syntax=syntax ) - report_native_answer(stage=NativeStage.REFERENCE_EXTRACTION, kind="reference_scans") return outcome diff --git a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py index efa9c139fd..dcfc1b6832 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py @@ -7,8 +7,6 @@ import sqlbuild._native as _native from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.planner.constants import ( MICROBATCH_END_SENTINEL, MICROBATCH_START_SENTINEL, @@ -35,7 +33,6 @@ def get_validated_model_cursor_intrinsics( (model_name, config_values.get("materialized"), config_values.get("cursor")), _PYTHON, ) - report_native_answer(stage=NativeStage.MODEL_LOOP, kind="cursor_intrinsic_validations") if error is not None: raise CompileInputError(error) return str(canonical_sql) @@ -47,7 +44,6 @@ def reject_cursor_intrinsics(*, sql: str, context: str) -> None: error: str | None = _native.cursor_intrinsics_rejection( sql, _RESERVED_MARKERS, context, _PYTHON ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="cursor_intrinsic_rejections") if error is not None: raise CompileInputError(error) diff --git a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py index 075bfd2a9c..27771f9300 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py @@ -32,10 +32,8 @@ EnumMember, ModelSchemaDeclaration, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.model_loop.constants import ( ENUM_REFERENCE_KIND_CODE, INVALID_CONSTANT_REFERENCE_STOP_CODE, @@ -165,7 +163,7 @@ def build_declaration_scope_resolver( if loaded_macros is not None: for macro in loaded_macros.values(): declarations[DeclarationIdentity(DeclarationKind.MACRO, macro.name)] = macro - if lookup is None and native_stage_enabled(NativeStage.DECLARATION_SCOPES): + if lookup is None: report_native_fallback(site=NativeFallbackSite.SCOPE_REBIND_LOOKUP) scope_lookup: ScopeLookup = build_scope_lookup(index=scope_index) if lookup is None else lookup return DeclarationScopeResolver( @@ -229,7 +227,6 @@ def resolve_declaration_context( context: DeclarationResolutionContext = resolver.native_contexts.context( list(matches), consumer ) - report_native_answer(stage=NativeStage.MODEL_LOOP, kind="declaration_contexts") if cache_key is not None: resolver.cache_context(key=cache_key, context=context) return context diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macro_argument_values.py b/src/sqlbuild/compiler/compile/_helpers/render/macro_argument_values.py index f6fd2b75ca..856cda760a 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macro_argument_values.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macro_argument_values.py @@ -16,8 +16,6 @@ ) from sqlbuild.compiler.compile.exceptions import MacroArgumentError from sqlbuild.compiler.compile.models import ParsedMacroArguments -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage type _ValueRow = tuple[object, ...] type _BuildContext = tuple[Sequence[object], Callable[..., MacroArgumentError]] @@ -74,7 +72,6 @@ def argument_error(*, offset: int, detail: str, help_text: str) -> MacroArgument help_text=cast(str, help_text), ) _, positional, keywords, references = parsed - report_native_answer(stage=NativeStage.MACRO_CALLS, kind="parsed_arguments") def unary_error(*, call: int, sign: str) -> MacroArgumentError: value: object = nested_values[call] diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macros.py b/src/sqlbuild/compiler/compile/_helpers/render/macros.py index c2475554b7..c562edff72 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macros.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macros.py @@ -55,9 +55,8 @@ DiscoveredMacroFile, EnumDeclaration, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( DECLARATION_READ_EVENT, @@ -1218,7 +1217,7 @@ def expand_sql_macros_result( consumer=consumer, ) bridge: MacroBridge | None = active_macro_bridge() - if bridge is None and MACRO_TOKEN in sql and native_stage_enabled(NativeStage.MACRO_CALLS): + if bridge is None and MACRO_TOKEN in sql: report_native_fallback( site=NativeFallbackSite.MACRO_UNBRIDGED_EXPANSION, kind="outside_compile_inputs" ) diff --git a/src/sqlbuild/compiler/compile/_helpers/render/parameters.py b/src/sqlbuild/compiler/compile/_helpers/render/parameters.py index 4dbb3e6c65..4aaaa82799 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/parameters.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/parameters.py @@ -6,14 +6,8 @@ from pathlib import Path import sqlbuild._native as _native -from sqlbuild.compiler.compile.constants import SQL_QUOTE_TOKENS from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.types import TypedSqlValueRenderer -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage -from sqlbuild.compiler.sql_analysis.main._skip_block_comment import skip_block_comment -from sqlbuild.compiler.sql_analysis.main._skip_line_comment import skip_line_comment -from sqlbuild.compiler.sql_analysis.main._skip_quoted_text import skip_quoted_text from sqlbuild.sql_values.exceptions import SqlValueRenderingError, SqlValueValidationError from sqlbuild.sql_values.main.validate_rendered_size import validate_rendered_sql_value_size from sqlbuild.sql_values.models import SqlValue @@ -36,71 +30,20 @@ def expand_test_parameters( """Render active parameter references while leaving comments and quoted text unchanged.""" value_lookup: dict[str, SqlValue] = dict(values) - if native_stage_enabled(NativeStage.ATTACHMENTS): - references, error = _native.scan_test_parameter_references( - sql, list(value_lookup), f"SQL test '{test_name}' case '{case_name}' in '{file_path}'" - ) - expanded: tuple[str, frozenset[str]] = _splice_parameter_references( - sql=sql, - references=references, - value_lookup=value_lookup, - value_renderer=value_renderer, - test_name=test_name, - case_name=case_name, - ) - if error is not None: - raise CompileInputError(error) - return expanded - used_names: set[str] = set() - parts: list[str] = [] - cursor: int = 0 - while cursor < len(sql): - character: str = sql[cursor] - if character in SQL_QUOTE_TOKENS: - end: int = skip_quoted_text(sql=sql, start=cursor) - parts.append(sql[cursor:end]) - cursor = end - continue - if sql.startswith("--", cursor): - end = skip_line_comment(sql=sql, start=cursor) - parts.append(sql[cursor:end]) - cursor = end - continue - if sql.startswith("/*", cursor): - end = skip_block_comment(sql=sql, start=cursor) - parts.append(sql[cursor:end]) - cursor = end - continue - if _PARAMETER_TOKEN.match(sql, cursor): - match: re.Match[str] | None = _PARAMETER_REFERENCE.match(sql, cursor) - if match is None: - raise CompileInputError( - f"SQL test '{test_name}' case '{case_name}' in '{file_path}' has malformed " - "@param reference; expected " - '@param("name")' - ) - name: str = match.group("name") - value: SqlValue | None = value_lookup.get(name) - if value is None: - raise CompileInputError( - f"SQL test '{test_name}' case '{case_name}' in '{file_path}' references " - f"undeclared parameter '{name}'" - ) - parts.append( - _render_parameter( - name=name, - value=value, - value_renderer=value_renderer, - test_name=test_name, - case_name=case_name, - ) - ) - used_names.add(name) - cursor = match.end() - continue - parts.append(character) - cursor += 1 - return "".join(parts), frozenset(used_names) + references, error = _native.scan_test_parameter_references( + sql, list(value_lookup), f"SQL test '{test_name}' case '{case_name}' in '{file_path}'" + ) + expanded: tuple[str, frozenset[str]] = _splice_parameter_references( + sql=sql, + references=references, + value_lookup=value_lookup, + value_renderer=value_renderer, + test_name=test_name, + case_name=case_name, + ) + if error is not None: + raise CompileInputError(error) + return expanded def _splice_parameter_references( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/templating.py b/src/sqlbuild/compiler/compile/_helpers/render/templating.py index ebbbe9bf52..e8c9e0eeaa 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/templating.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/templating.py @@ -8,8 +8,6 @@ from sqlbuild.compiler.compile.classes.unicode_environment import UnicodeEnvironment from sqlbuild.compiler.compile.constants import COMPILE_INPUT_READS, TEMPLATE_OPEN_TOKEN from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.model_config.constants import ENVIRONMENT_READ from sqlbuild.compiler.model_config.main._expand_native_config_templates import ( expand_native_config_templates, @@ -31,7 +29,6 @@ def expand_effective_vars(raw_values: dict[str, object]) -> dict[str, object]: raw_values, UnicodeEnvironment() ) record_template_reads(tuple(outcome[1])) - report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="effective_vars") if isinstance(outcome[0], _native.NativeConfigError): raise native_config_error(error=outcome[0]) return cast(dict[str, object], outcome[0]) @@ -96,7 +93,6 @@ def expand_template_data( context_label=context_label, ) record_template_reads(outcome.reads) - report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="template_expansions") if isinstance(outcome, NativeTemplateRejection): raise native_config_error(error=outcome.error) return outcome.value diff --git a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py index 603a3d8280..1cc9d267a3 100644 --- a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py @@ -22,8 +22,6 @@ CompileSqlScenarioCte, CompileSqlScenarioCtes, ) -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax _CONTEXT: str = "SQL scenario" @@ -40,7 +38,6 @@ def extract_sql_scenario_ctes( outcome: dict[str, Any] = orjson.loads( _native.extract_sql_scenario_json(sql, file_label, syntax.native_mapping) ) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="scenario_ctes") error: str | None = outcome.get("error") if error is not None: raise CompileInputError(error) diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py index 41933e4fe5..c76b043fa5 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py @@ -24,8 +24,6 @@ SqlReferenceOrigin, ) from sqlbuild.compiler.compile.types import SqlTestMode -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren from sqlbuild.compiler.sql_analysis.main._is_identifier_character import ( @@ -143,7 +141,6 @@ def complete_omitted_ceremonial_select(*, sql: str, syntax: SqlLexicalSyntax) -> """Return test or scenario SQL as one statement, adding an omitted trailing `SELECT 1`.""" offset: int | None = _native.omitted_ceremonial_select(sql, syntax.native_mapping) - report_native_answer(stage=NativeStage.ATTACHMENTS, kind="omitted_select_scans") if offset is None: return sql return f"{sql[:offset]}{OMITTED_CEREMONIAL_SELECT_SQL}{sql[offset:]}" diff --git a/src/sqlbuild/compiler/compile/main/_assemble_project.py b/src/sqlbuild/compiler/compile/main/_assemble_project.py index 09b05d4715..e3fffde3ae 100644 --- a/src/sqlbuild/compiler/compile/main/_assemble_project.py +++ b/src/sqlbuild/compiler/compile/main/_assemble_project.py @@ -25,8 +25,6 @@ CompilerDiagnostic, PythonSqlReferenceReport, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.lineage.types import ColumnLineageMode from sqlbuild.compiler.macro_bridge.main.active_macro_bridge import active_macro_bridge from sqlbuild.compiler.macro_bridge.main.run_with_macro_bridge import run_with_macro_bridge @@ -54,9 +52,7 @@ def assemble_project( analysis_model_names=analysis_model_names, ) project: CompiledProject = ( - run_with_macro_bridge(stage=assemble) - if native_stage_enabled(NativeStage.MACRO_CALLS) and active_macro_bridge() is None - else assemble() + run_with_macro_bridge(stage=assemble) if active_macro_bridge() is None else assemble() ) python_sql: PythonSqlReferenceReport = ( python_sql_reference_diagnostics( diff --git a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py index 2b1771d007..7b2cc10029 100644 --- a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py +++ b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py @@ -11,9 +11,8 @@ ExpansionSpan, SqlExpansionContext, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite def expand_sql_with_spans( @@ -39,8 +38,7 @@ def expand_sql_with_spans( inaccessible_enums=declarations.inaccessible_enums, inaccessible_constants=declarations.inaccessible_constants, ) - if native_stage_enabled(NativeStage.MODEL_LOOP): - report_native_fallback(site=NativeFallbackSite.LINT_EXPANSION) + report_native_fallback(site=NativeFallbackSite.LINT_EXPANSION) return expand_authored_sql_with_spans( sql=sql, file_path=file_path, diff --git a/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py b/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py index 5ed42e44b8..d708f68acf 100644 --- a/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py +++ b/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py @@ -25,10 +25,7 @@ requires_contract_evaluation, ) from sqlbuild.compiler.contracts._helpers.native_deferrals import record_contract_deferral -from sqlbuild.compiler.contracts.constants import ( - NATIVE_CONTRACTS_DEFERRAL_SITE, - NATIVE_CONTRACTS_UNSUPPORTED_INPUT, -) +from sqlbuild.compiler.contracts.constants import NATIVE_CONTRACTS_DEFERRAL_SITE from sqlbuild.compiler.contracts.models import ContractValidationResult from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.spec.contracts.models import SchemaColumn, SchemaModelEntry, SourceLocation @@ -53,14 +50,9 @@ def native_model_contracts( *, project: CompiledProject, dialect: TypeDialect | str | None -) -> ContractValidationResult | None: +) -> ContractValidationResult: """Return `evaluate_model_contracts`'s result, with deferred models evaluated by Python.""" - if not isinstance(dialect, str | None): - record_contract_deferral( - kind=NATIVE_CONTRACTS_UNSUPPORTED_INPUT, site=NATIVE_CONTRACTS_DEFERRAL_SITE - ) - return None mode: ColumnContractMode = project.settings.column_contract_mode outcomes: list[tuple[str | None, list[_DiagnosticRow]]] = ( _native.evaluate_native_model_contracts( diff --git a/src/sqlbuild/compiler/contracts/_helpers/native_promotion.py b/src/sqlbuild/compiler/contracts/_helpers/native_promotion.py index a345650b26..c143956477 100644 --- a/src/sqlbuild/compiler/contracts/_helpers/native_promotion.py +++ b/src/sqlbuild/compiler/contracts/_helpers/native_promotion.py @@ -3,31 +3,22 @@ from __future__ import annotations import sqlbuild._native as _native +from sqlbuild.adapter.contract.types import TablePromotionMode from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject, CompilerDiagnostic from sqlbuild.compiler.compile.types import ( CompiledResourceType, DiagnosticPhase, DiagnosticSeverity, ) -from sqlbuild.compiler.contracts._helpers.native_deferrals import record_contract_deferral -from sqlbuild.compiler.contracts.constants import ( - NATIVE_CONTRACTS_UNSUPPORTED_INPUT, - NATIVE_PROMOTION_DEFERRAL_SITE, -) from sqlbuild.compiler.planner.types import MaterializationType def native_promotion_conflicts( - *, project: CompiledProject, adapter_default: object, settings_file: str -) -> tuple[CompilerDiagnostic, ...] | None: - """Return `promotion_conflict_diagnostics`'s result, or None where Python must build it.""" + *, project: CompiledProject, adapter_default: TablePromotionMode, settings_file: str +) -> tuple[CompilerDiagnostic, ...]: + """Return one K011 error per enforced-contract table model under immediate promotion.""" - explicit_mode: object = project.settings.table_promotion_mode - if not isinstance(adapter_default, str) or not isinstance(explicit_mode, str | None): - record_contract_deferral( - kind=NATIVE_CONTRACTS_UNSUPPORTED_INPUT, site=NATIVE_PROMOTION_DEFERRAL_SITE - ) - return None + explicit_mode: str | None = project.settings.table_promotion_mode conflicts: list[tuple[int, str, str, str]] = _native.native_promotion_conflicts( ( explicit_mode, diff --git a/src/sqlbuild/compiler/contracts/_helpers/promotion.py b/src/sqlbuild/compiler/contracts/_helpers/promotion.py deleted file mode 100644 index 62d948a78f..0000000000 --- a/src/sqlbuild/compiler/contracts/_helpers/promotion.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Compile-time conflicts between enforced contracts and immediate table promotion.""" - -from __future__ import annotations - -from sqlbuild.adapter.contract.types import TablePromotionMode -from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject, CompilerDiagnostic -from sqlbuild.compiler.compile.types import ( - CompiledResourceType, - DiagnosticPhase, - DiagnosticSeverity, -) -from sqlbuild.compiler.discovery.constants import ( - SETTINGS_SECTION, - STAGED_PROMOTION_MODE, - TABLE_PROMOTION_MODE_SETTING_KEY, -) -from sqlbuild.compiler.planner.types import ContractPolicy, IncrementalMode, MaterializationType -from sqlbuild.errors.setting_help.main.join_helps import join_helps -from sqlbuild.errors.setting_help.main.setting_help import setting_help -from sqlbuild.errors.setting_help.main.setting_note import setting_note -from sqlbuild.errors.setting_help.main.snippet_help import snippet_help - -_PROMOTION_CONFLICT_CODE: str = "K011" -_STAGED_LIFECYCLE_MATERIALIZATIONS: frozenset[MaterializationType] = frozenset( - {MaterializationType.TABLE, MaterializationType.INCREMENTAL} -) - - -def promotion_conflict_diagnostics_impl( - *, - project: CompiledProject, - adapter_default: TablePromotionMode, - settings_file: str, -) -> tuple[CompilerDiagnostic, ...]: - """Report each enforced-contract table model when promotion is immediate.""" - - explicit_mode: str | None = project.settings.table_promotion_mode - effective_mode: str = explicit_mode if explicit_mode is not None else adapter_default - if effective_mode != TablePromotionMode.IMMEDIATE: - return () - help_text: str = _conflict_help( - explicit=explicit_mode is not None, - adapter_default=adapter_default, - settings_file=settings_file, - ) - return tuple( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_PROMOTION_CONFLICT_CODE, - message=( - f"model '{model.name}': contract enforced requires staged table promotion; " - "immediate table promotion cannot validate runtime output before target mutation" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - path=model.relative_path, - help=help_text, - ) - for model in project.models - if _uses_staged_table_lifecycle(model) - ) - - -def _uses_staged_table_lifecycle(model: CompiledModel) -> bool: - values: dict[str, object] = model.config.values - if values.get("contract") != ContractPolicy.ENFORCED: - return False - materialized: object = values.get("materialized", MaterializationType.TABLE) - return ( - materialized in _STAGED_LIFECYCLE_MATERIALIZATIONS - and values.get("incremental_mode") != IncrementalMode.MICROBATCH - ) - - -def _conflict_help( - *, explicit: bool, adapter_default: TablePromotionMode, settings_file: str -) -> str: - current: str = ( - setting_note( - file_name=settings_file, - section=SETTINGS_SECTION, - key=TABLE_PROMOTION_MODE_SETTING_KEY, - value=TablePromotionMode.IMMEDIATE.value, - explicit=explicit, - ) - + "; enforced contracts are validated in a staging table before promotion" - ) - staged_fix: str = ( - snippet_help( - purpose="remove that line to use the default staged promotion", - target=f"or set this in {settings_file}", - lines=( - f"[{SETTINGS_SECTION}]", - f'{TABLE_PROMOTION_MODE_SETTING_KEY} = "{STAGED_PROMOTION_MODE}"', - ), - ) - if explicit and adapter_default == TablePromotionMode.STAGED - else setting_help( - purpose="to validate enforced contracts before promotion", - file_name=settings_file, - section=SETTINGS_SECTION, - key=TABLE_PROMOTION_MODE_SETTING_KEY, - value=STAGED_PROMOTION_MODE, - ) - ) - return join_helps( - current, - staged_fix, - "or remove the model's enforced contract: `contract enforced` in its MODEL header, or " - '`contract = "enforced"` in the [defaults] or [path_defaults] entry that applies to it', - ) diff --git a/src/sqlbuild/compiler/contracts/constants.py b/src/sqlbuild/compiler/contracts/constants.py index d51f5c1015..a96e4cdb03 100644 --- a/src/sqlbuild/compiler/contracts/constants.py +++ b/src/sqlbuild/compiler/contracts/constants.py @@ -2,5 +2,3 @@ NOT_NULL_AUDIT_NAME: str = "not_null" NATIVE_CONTRACTS_DEFERRAL_SITE: str = "contracts/columns.py" -NATIVE_PROMOTION_DEFERRAL_SITE: str = "contracts/promotion.py" -NATIVE_CONTRACTS_UNSUPPORTED_INPUT: str = "unsupported_input" diff --git a/src/sqlbuild/compiler/contracts/main/_evaluate_native_model_contracts.py b/src/sqlbuild/compiler/contracts/main/_evaluate_native_model_contracts.py index c35b77e9ef..e9e0e4489f 100644 --- a/src/sqlbuild/compiler/contracts/main/_evaluate_native_model_contracts.py +++ b/src/sqlbuild/compiler/contracts/main/_evaluate_native_model_contracts.py @@ -1,4 +1,4 @@ -"""Evaluate model contracts natively for the preview compiler engine.""" +"""Evaluate model contracts natively.""" from __future__ import annotations @@ -10,7 +10,7 @@ def evaluate_native_model_contracts( *, project: CompiledProject, dialect: TypeDialect | str | None -) -> ContractValidationResult | None: - """Return the contract diagnostics, or None where Python must evaluate the contracts.""" +) -> ContractValidationResult: + """Return the contract diagnostics; Python evaluates only the models native defers.""" return native_model_contracts(project=project, dialect=dialect) diff --git a/src/sqlbuild/compiler/contracts/main/_native_promotion_conflicts.py b/src/sqlbuild/compiler/contracts/main/_native_promotion_conflicts.py index af639c188d..0327b490a2 100644 --- a/src/sqlbuild/compiler/contracts/main/_native_promotion_conflicts.py +++ b/src/sqlbuild/compiler/contracts/main/_native_promotion_conflicts.py @@ -1,15 +1,16 @@ -"""Find promotion conflicts natively for the preview compiler engine.""" +"""Find promotion conflicts natively.""" from __future__ import annotations +from sqlbuild.adapter.contract.types import TablePromotionMode from sqlbuild.compiler.compile.models import CompiledProject, CompilerDiagnostic from sqlbuild.compiler.contracts._helpers.native_promotion import native_promotion_conflicts def native_promotion_conflict_diagnostics( - *, project: CompiledProject, adapter_default: object, settings_file: str -) -> tuple[CompilerDiagnostic, ...] | None: - """Return the K011 promotion conflicts, or None where Python must find them.""" + *, project: CompiledProject, adapter_default: TablePromotionMode, settings_file: str +) -> tuple[CompilerDiagnostic, ...]: + """Return the K011 promotion conflicts.""" return native_promotion_conflicts( project=project, adapter_default=adapter_default, settings_file=settings_file diff --git a/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py b/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py index 33edbfd775..5e0745a85d 100644 --- a/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py +++ b/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py @@ -4,13 +4,9 @@ from sqlbuild.adapter.contract.types import TablePromotionMode from sqlbuild.compiler.compile.models import CompiledProject, CompilerDiagnostic -from sqlbuild.compiler.contracts._helpers.promotion import promotion_conflict_diagnostics_impl from sqlbuild.compiler.contracts.main._native_promotion_conflicts import ( native_promotion_conflict_diagnostics, ) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage def promotion_conflict_diagnostics( @@ -21,13 +17,6 @@ def promotion_conflict_diagnostics( ) -> tuple[CompilerDiagnostic, ...]: """Return one K011 error per enforced-contract table model under immediate promotion.""" - if native_stage_enabled(NativeStage.CONTRACTS): - native: tuple[CompilerDiagnostic, ...] | None = native_promotion_conflict_diagnostics( - project=project, adapter_default=adapter_default, settings_file=settings_file - ) - if native is not None: - report_native_answer(stage=NativeStage.CONTRACTS, kind="promotion_checks") - return native - return promotion_conflict_diagnostics_impl( + return native_promotion_conflict_diagnostics( project=project, adapter_default=adapter_default, settings_file=settings_file ) diff --git a/src/sqlbuild/compiler/contracts/main/validate.py b/src/sqlbuild/compiler/contracts/main/validate.py index a51a6dda3b..548ac16eec 100644 --- a/src/sqlbuild/compiler/contracts/main/validate.py +++ b/src/sqlbuild/compiler/contracts/main/validate.py @@ -3,19 +3,11 @@ from __future__ import annotations from sqlbuild.adapter.contract.types import TypeDialect -from sqlbuild.compiler.compile.models import CompiledProject, CompilerDiagnostic -from sqlbuild.compiler.contracts._helpers.evaluation import ( - python_model_contract_diagnostics, - requires_contract_evaluation, -) +from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.contracts.main._evaluate_native_model_contracts import ( evaluate_native_model_contracts, ) from sqlbuild.compiler.contracts.models import ContractValidationResult -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage -from sqlbuild.spec.contracts.types import ColumnContractMode def evaluate_model_contracts( @@ -25,21 +17,4 @@ def evaluate_model_contracts( ) -> ContractValidationResult: """Evaluate model header column contracts against inferred output columns.""" - if native_stage_enabled(NativeStage.CONTRACTS): - native_result: ContractValidationResult | None = evaluate_native_model_contracts( - project=project, dialect=dialect - ) - if native_result is not None: - report_native_answer(stage=NativeStage.CONTRACTS, kind="contract_validations") - return native_result - mode: ColumnContractMode = project.settings.column_contract_mode - if not any(requires_contract_evaluation(model=model, mode=mode) for model in project.models): - return ContractValidationResult(diagnostics=()) - - diagnostics: list[CompilerDiagnostic] = [] - for model in project.models: - if requires_contract_evaluation(model=model, mode=mode): - diagnostics.extend( - python_model_contract_diagnostics(model=model, mode=mode, dialect=dialect) - ) - return ContractValidationResult(diagnostics=tuple(diagnostics)) + return evaluate_native_model_contracts(project=project, dialect=dialect) diff --git a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py index 6c1bfba3d9..d06ee98589 100644 --- a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py +++ b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py @@ -48,8 +48,6 @@ ModelSchemaDeclaration, ) from sqlbuild.compiler.discovery.types import NativeFileScope, NativeLocation, NativeScopeFields -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.scopes.types import DeclarationKind, ScopeKind type _NativeDeclarationFile = tuple[str, NativeFileScope | None, tuple[object, ...]] @@ -178,7 +176,6 @@ def native_seed_files(*, project_dir: Path) -> tuple[DiscoveredSeedFile, ...]: DiscoveredSeedFile(file_path=project_dir / relative_path, relative_path=Path(relative_path)) for relative_path, _scope, _payload in _native_files(project_dir=project_dir, kind="seed") ) - report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="seed_files", units=len(seeds)) return seeds @@ -259,12 +256,10 @@ def _record[RecordT]( scope, payload = item tag: object = payload[0] if tag == NATIVE_PARSED_TAG: - report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="parsed_files") return build( project_dir=project_dir, relative_path=relative_path, scope=scope, payload=payload ) if tag == NATIVE_FAILED_TAG: - report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="failed_files") raise native_failure(payload) raise native_read_error(payload=payload, file_path=project_dir / relative_path) diff --git a/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py b/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py index ee78a71c88..4c166f2493 100644 --- a/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py +++ b/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py @@ -28,8 +28,6 @@ ) from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile, DiscoveryFileFault from sqlbuild.compiler.discovery.types import NativeFiles, NativeLocation -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage def discover_native_model_files( @@ -60,7 +58,6 @@ def discover_native_model_files( project_dir=project_dir, ) seed_snapshot_listings(project_dir=project_dir, tree=tree) - report_native_answer(stage=NativeStage.DISCOVERY, kind="model_file_listings") return materialise_native_files( project_dir=project_dir, files=( diff --git a/src/sqlbuild/compiler/frontier/_helpers/engine.py b/src/sqlbuild/compiler/frontier/_helpers/engine.py index 93b9cb5ff0..33a7850b0c 100644 --- a/src/sqlbuild/compiler/frontier/_helpers/engine.py +++ b/src/sqlbuild/compiler/frontier/_helpers/engine.py @@ -6,6 +6,7 @@ COMPILER_ENGINE_ENV_VAR, COMPILER_ENGINE_VALUES, DEFAULT_COMPILER_ENGINE, + REMOVED_PYTHON_ENGINE, ) from sqlbuild.compiler.frontier.exceptions import CompilerEngineError from sqlbuild.compiler.frontier.types import CompilerEngine @@ -17,10 +18,22 @@ def active_compiler_engine() -> CompilerEngine: raw_value: str | None = os.environ.get(COMPILER_ENGINE_ENV_VAR) if not raw_value: return DEFAULT_COMPILER_ENGINE + return parse_compiler_engine(raw_value=raw_value, source=COMPILER_ENGINE_ENV_VAR) + + +def parse_compiler_engine(*, raw_value: str, source: str) -> CompilerEngine: + """Return the engine `raw_value` names, explaining the removed Python engine.""" + + if raw_value == REMOVED_PYTHON_ENGINE: + raise CompilerEngineError( + f"{source} {REMOVED_PYTHON_ENGINE!r} is no longer supported: SQLBuild removed its " + "Python compiler, and the native compiler is the default. Unset " + f"{COMPILER_ENGINE_ENV_VAR} and drop --compiler-engine to use it; to run the " + "Python compiler for comparison, pin an earlier SQLBuild release." + ) try: return CompilerEngine(raw_value) except ValueError as error: raise CompilerEngineError( - f"{COMPILER_ENGINE_ENV_VAR} must be one of {', '.join(COMPILER_ENGINE_VALUES)} " - f"(got {raw_value!r})" + f"{source} must be one of {', '.join(COMPILER_ENGINE_VALUES)} (got {raw_value!r})" ) from error diff --git a/src/sqlbuild/compiler/frontier/_helpers/native_frontier.py b/src/sqlbuild/compiler/frontier/_helpers/native_frontier.py index 031f90ca47..1505be9c60 100644 --- a/src/sqlbuild/compiler/frontier/_helpers/native_frontier.py +++ b/src/sqlbuild/compiler/frontier/_helpers/native_frontier.py @@ -2,8 +2,7 @@ from collections.abc import Callable -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import CompilerStage, NativeStage +from sqlbuild.compiler.frontier.types import CompilerStage from sqlbuild.compiler.macro_bridge.main.run_with_macro_bridge import run_with_macro_bridge @@ -17,8 +16,6 @@ def native_frontier[T]( if native_stage is not None: return native_stage() - if until is CompilerStage.COMPILE_PROJECT_INPUTS and native_stage_enabled( - NativeStage.MACRO_CALLS - ): + if until is CompilerStage.COMPILE_PROJECT_INPUTS: return run_with_macro_bridge(stage=python_stage) return python_stage() diff --git a/src/sqlbuild/compiler/frontier/constants.py b/src/sqlbuild/compiler/frontier/constants.py index cbb99ff7bb..6dad7ec4e2 100644 --- a/src/sqlbuild/compiler/frontier/constants.py +++ b/src/sqlbuild/compiler/frontier/constants.py @@ -10,31 +10,14 @@ COMPILER_ENGINE_VALUES: tuple[str, ...] = tuple(engine.value for engine in CompilerEngine) NATIVE_CACHE_NAMESPACE_SUFFIX: str = "-native-v1" NATIVE_PREVIEW_CACHE_NAMESPACE_SUFFIX: str = "-native-preview-v1" +REMOVED_PYTHON_ENGINE: str = "python" ENGINE_CACHE_NAMESPACE_SUFFIXES: dict[CompilerEngine, str] = { - CompilerEngine.PYTHON: "", CompilerEngine.NATIVE: NATIVE_CACHE_NAMESPACE_SUFFIX, CompilerEngine.NATIVE_PREVIEW: NATIVE_PREVIEW_CACHE_NAMESPACE_SUFFIX, } -NATIVE_STAGE_TIERS: dict[NativeStage, NativeStageTier] = { - NativeStage.DISCOVERY: NativeStageTier.SHIPPED, - NativeStage.DECLARATION_SCOPES: NativeStageTier.SHIPPED, - NativeStage.MODEL_CONFIG: NativeStageTier.SHIPPED, - NativeStage.REFERENCE_EXTRACTION: NativeStageTier.SHIPPED, - NativeStage.DECLARATION_FILES: NativeStageTier.SHIPPED, - NativeStage.MODEL_LOOP: NativeStageTier.SHIPPED, - NativeStage.MACRO_CALLS: NativeStageTier.SHIPPED, - NativeStage.MACRO_CALL_STORE: NativeStageTier.SHIPPED, - NativeStage.ATTACHMENTS: NativeStageTier.SHIPPED, - NativeStage.TYPE_SYSTEM: NativeStageTier.SHIPPED, - NativeStage.MODEL_ANALYSIS: NativeStageTier.SHIPPED, - NativeStage.SEMANTIC_CHECKS: NativeStageTier.SHIPPED, - NativeStage.CONTRACTS: NativeStageTier.SHIPPED, - NativeStage.LINEAGE_FACTS: NativeStageTier.SHIPPED, - NativeStage.SQL_TEST_GLUE: NativeStageTier.SHIPPED, - NativeStage.PROJECT_ASSEMBLY: NativeStageTier.SHIPPED, -} +RETIRED_CACHE_NAMESPACE_SUFFIXES: tuple[str, ...] = ("", *ENGINE_CACHE_NAMESPACE_SUFFIXES.values()) +NATIVE_STAGE_TIERS: dict[NativeStage, NativeStageTier] = {} ENGINE_NATIVE_STAGE_TIERS: dict[CompilerEngine, frozenset[NativeStageTier]] = { - CompilerEngine.PYTHON: frozenset(), CompilerEngine.NATIVE: frozenset({NativeStageTier.SHIPPED}), CompilerEngine.NATIVE_PREVIEW: frozenset({NativeStageTier.SHIPPED, NativeStageTier.PREVIEW}), } diff --git a/src/sqlbuild/compiler/frontier/main/_compile_frontier.py b/src/sqlbuild/compiler/frontier/main/_compile_frontier.py index 9dee821b4e..c815c36c8d 100644 --- a/src/sqlbuild/compiler/frontier/main/_compile_frontier.py +++ b/src/sqlbuild/compiler/frontier/main/_compile_frontier.py @@ -1,11 +1,10 @@ -"""The single seam between the Python compiler stages and the native frontier.""" +"""The single seam between the compiler stages and the native frontier.""" from collections.abc import Callable -from sqlbuild.compiler.frontier._helpers.engine import active_compiler_engine from sqlbuild.compiler.frontier._helpers.native_frontier import native_frontier from sqlbuild.compiler.frontier._helpers.stage_capture import write_stage_capture -from sqlbuild.compiler.frontier.types import CompilerEngine, CompilerStage +from sqlbuild.compiler.frontier.types import CompilerStage def compile_frontier[T]( @@ -14,12 +13,8 @@ def compile_frontier[T]( python_stage: Callable[[], T], native_stage: Callable[[], T] | None = None, ) -> T: - """Produce the frontier object for `until` on the active engine, capturing it on request.""" + """Produce the frontier object for `until`, capturing it on request.""" - result: T = ( - python_stage() - if active_compiler_engine() is CompilerEngine.PYTHON - else native_frontier(until=until, python_stage=python_stage, native_stage=native_stage) - ) + result: T = native_frontier(until=until, python_stage=python_stage, native_stage=native_stage) write_stage_capture(stage=until, value=result) return result diff --git a/src/sqlbuild/compiler/frontier/main/parse_compiler_engine.py b/src/sqlbuild/compiler/frontier/main/parse_compiler_engine.py new file mode 100644 index 0000000000..584dbac292 --- /dev/null +++ b/src/sqlbuild/compiler/frontier/main/parse_compiler_engine.py @@ -0,0 +1,10 @@ +"""Public compiler engine value parsing entry point.""" + +from sqlbuild.compiler.frontier._helpers.engine import parse_compiler_engine as _parse +from sqlbuild.compiler.frontier.types import CompilerEngine + + +def parse_compiler_engine(*, raw_value: str, source: str) -> CompilerEngine: + """Return the engine `raw_value` names, raising CompilerEngineError naming `source`.""" + + return _parse(raw_value=raw_value, source=source) diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index bfe1a2857c..67a894c002 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -6,30 +6,12 @@ class CompilerEngine(StrEnum): """Which compiler implementation produces the frontier objects.""" - PYTHON = "python" NATIVE = "native" NATIVE_PREVIEW = "native-preview" class NativeStage(StrEnum): - """A native implementation that replaces one Python compiler stage when its tier is active.""" - - DISCOVERY = "discovery" - DECLARATION_SCOPES = "declaration_scopes" - MODEL_CONFIG = "model_config" - REFERENCE_EXTRACTION = "reference_extraction" - DECLARATION_FILES = "declaration_files" - MODEL_LOOP = "model_loop" - MACRO_CALLS = "macro_calls" - MACRO_CALL_STORE = "macro_call_store" - ATTACHMENTS = "attachments" - TYPE_SYSTEM = "type_system" - MODEL_ANALYSIS = "model_analysis" - SEMANTIC_CHECKS = "semantic_checks" - CONTRACTS = "contracts" - LINEAGE_FACTS = "lineage_facts" - SQL_TEST_GLUE = "sql_test_glue" - PROJECT_ASSEMBLY = "project_assembly" + """A native implementation behind the preview engine until its tier is shipped.""" class NativeStageTier(StrEnum): diff --git a/src/sqlbuild/compiler/lineage/main/columns.py b/src/sqlbuild/compiler/lineage/main/columns.py index 735a03c3d2..6bb3079ca7 100644 --- a/src/sqlbuild/compiler/lineage/main/columns.py +++ b/src/sqlbuild/compiler/lineage/main/columns.py @@ -3,9 +3,6 @@ from __future__ import annotations from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.lineage._helpers.fast_columns import ( build_fast_project_column_lineage, ) @@ -30,13 +27,11 @@ def build_project_column_lineage( match mode: case ColumnLineageMode.FAST: - if native_stage_enabled(NativeStage.LINEAGE_FACTS): - native_lineage: ProjectColumnLineage | None = build_native_column_lineage( - project=project, dialect=dialect, model_names=model_names - ) - if native_lineage is not None: - report_native_answer(stage=NativeStage.LINEAGE_FACTS, kind="lineage_graphs") - return native_lineage + native_lineage: ProjectColumnLineage | None = build_native_column_lineage( + project=project, dialect=dialect, model_names=model_names + ) + if native_lineage is not None: + return native_lineage return build_fast_project_column_lineage( project=project, dialect=dialect, diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index 1d53c9869c..3a38731cee 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -9,9 +9,8 @@ from contextvars import Token import sqlbuild._native as _native -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS @@ -35,7 +34,6 @@ def run_with_macro_bridge[T](*, stage: Callable[[], T]) -> T: STORE_MODULE_PATHS.update(bridge.store_module_paths()) hits, misses, recorded = bridge.stats() store_hits, store_records = bridge.store_stats() - report_native_answer(stage=NativeStage.MACRO_CALL_STORE, kind="served_calls", units=store_hits) logging.getLogger(__name__).debug( "Native macro bridge: %d memo hits, %d store hits, %d misses, %d recorded calls, " "%d stored calls", diff --git a/src/sqlbuild/compiler/planner/_helpers/sql_tests/native_planning.py b/src/sqlbuild/compiler/planner/_helpers/sql_tests/native_planning.py index 54c533f253..44df9df303 100644 --- a/src/sqlbuild/compiler/planner/_helpers/sql_tests/native_planning.py +++ b/src/sqlbuild/compiler/planner/_helpers/sql_tests/native_planning.py @@ -3,7 +3,6 @@ from __future__ import annotations import time -from typing import Any, cast import orjson @@ -18,11 +17,7 @@ CompiledModelSqlTestPayload, CompiledProject, CompiledSqlTest, - CompileSqlTestCte, ) -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.planner._helpers.sql_tests.cursor_window import ( declared_window_cursor_models, declares_cursor_window, @@ -39,7 +34,6 @@ from sqlbuild.compiler.planner.types import WarningSeverity from sqlbuild.compiler.profiling.classes.context import CompileTimingContext from sqlbuild.compiler.profiling.models import CompileTimingCollector -from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax from sqlbuild.compiler.sql_test_glue.constants import CURSOR_INTRINSIC_NAMES from sqlbuild.compiler.sql_test_glue.models import ( NativeSqlTestChainRequest, @@ -50,7 +44,6 @@ NativeChainStepRow, NativeSqlTestPlanRow, ) -from sqlbuild.executor.testing.types import NativeSqlTestRenderingModule _UNRESOLVED_REFERENCE_PREFIX: str = "sql_test_reference:" @@ -58,36 +51,6 @@ _NATIVE_WORKERS: int = 4 -def plan_and_render_sql_test_artifacts( - *, - project: CompiledProject, - tests: tuple[CompiledSqlTest, ...], - adapter: BaseAdapter, - sql_analysis_enabled: bool, -) -> tuple[NativeSqlTestArtifact, ...]: - """Plan and render SQL tests in one deterministic native project call.""" - - artifacts: list[NativeSqlTestArtifact] = [] - for plan in _plan_sql_tests_from_json( - project=project, - tests=tests, - adapter=adapter, - sql_analysis_enabled=sql_analysis_enabled, - render_sql=True, - include_plan=False, - ): - if plan.sql is None: - raise NativeSqlTestPlanningError("native SQL-test planning omitted rendered SQL") - artifacts.append( - NativeSqlTestArtifact( - sql=plan.sql, - model_names=plan.model_names, - error_messages=plan.error_messages, - ) - ) - return tuple(artifacts) - - def plan_compiled_sql_test_artifacts( *, project: CompiledProject, @@ -137,16 +100,7 @@ def plan_sql_tests_natively( ) -> tuple[NativeSqlTestPlan, ...]: """Plan SQL-test chains and assertions in one native batch, optionally rendering SQL.""" - if native_stage_enabled(NativeStage.SQL_TEST_GLUE): - return _plan_compiled_sql_tests( - project=project, - tests=tests, - adapter=adapter, - sql_analysis_enabled=sql_analysis_enabled, - render_sql=render_sql, - include_plan=include_plan, - ) - return _plan_sql_tests_from_json( + return _plan_compiled_sql_tests( project=project, tests=tests, adapter=adapter, @@ -307,65 +261,6 @@ def _record_planning_timing(*, start_ns: int, planning_ns: int, rendering_ns: in collector.add(phase="comparison_render_ms", elapsed_ns=rendering_ns) -def _plan_sql_tests_from_json( - *, - project: CompiledProject, - tests: tuple[CompiledSqlTest, ...], - adapter: BaseAdapter, - sql_analysis_enabled: bool, - render_sql: bool, - include_plan: bool, -) -> tuple[NativeSqlTestPlan, ...]: - if not tests: - return () - start_ns: int = time.perf_counter_ns() - request: dict[str, object] = { - "models": _model_requests(project=project, adapter=adapter), - "functions": _function_requests(project=project, adapter=adapter), - "tests": _planning_test_requests(project=project, tests=tests, adapter=adapter), - "sqlAnalysisEnabled": sql_analysis_enabled, - "sqlAnalysisDialect": adapter.sql_analysis_dialect(), - "setDifferenceOperator": adapter.render_set_difference_operator(), - "requiresDerivedTableAliases": adapter.requires_derived_table_aliases(), - "lexicalSyntax": _lexical_syntax_request(project.sql_lexical_syntax), - "workers": _NATIVE_WORKERS, - "renderSql": render_sql, - "includePlan": include_plan, - } - try: - native_response: str = cast( - NativeSqlTestRenderingModule, _native - ).plan_and_render_sql_tests_json( - orjson.dumps(request, option=orjson.OPT_SORT_KEYS).decode() - ) - except ValueError as error: - raise _planning_error(error=error, tests=tests) from None - response_payload: object = orjson.loads(native_response) - if not isinstance(response_payload, dict): - raise NativeSqlTestPlanningError( - "native SQL-test planning returned an invalid batch response" - ) - response: object = response_payload.get("artifacts") - planning_ns: object = response_payload.get("planningNs") - rendering_ns: object = response_payload.get("renderingNs") - if ( - not isinstance(response, list) - or len(response) != len(tests) - or not isinstance(planning_ns, int) - or isinstance(planning_ns, bool) - or not isinstance(rendering_ns, int) - or isinstance(rendering_ns, bool) - ): - raise NativeSqlTestPlanningError( - "native SQL-test planning returned an invalid batch response" - ) - plans: tuple[NativeSqlTestPlan, ...] = tuple( - _plan_from_payload(value=value) for value in response - ) - _record_planning_timing(start_ns=start_ns, planning_ns=planning_ns, rendering_ns=rendering_ns) - return plans - - def resolve_sql_test_model_chains( *, project: CompiledProject, tests: tuple[CompiledSqlTest, ...] ) -> tuple[tuple[str, ...], ...]: @@ -373,36 +268,17 @@ def resolve_sql_test_model_chains( if not tests: return () - if native_stage_enabled(NativeStage.SQL_TEST_GLUE): - try: - native_chains: list[list[str]] = _native.resolve_compiled_sql_test_chains( - NativeSqlTestChainRequest( - models=project.models, - tests=tests, - lexical_syntax=project.sql_lexical_syntax.native_mapping, - ) - ) - except ValueError as error: - raise _planning_error(error=error, tests=tests) from None - return tuple(tuple(chain) for chain in native_chains) - request: dict[str, object] = { - "models": _chain_model_requests(project=project), - "tests": [_test_request(test=test) for test in tests], - "lexicalSyntax": _lexical_syntax_request(project.sql_lexical_syntax), - } try: - native_response: str = cast( - NativeSqlTestRenderingModule, _native - ).resolve_sql_test_chains_json(orjson.dumps(request, option=orjson.OPT_SORT_KEYS).decode()) + native_chains: list[list[str]] = _native.resolve_compiled_sql_test_chains( + NativeSqlTestChainRequest( + models=project.models, + tests=tests, + lexical_syntax=project.sql_lexical_syntax.native_mapping, + ) + ) except ValueError as error: raise _planning_error(error=error, tests=tests) from None - response_payload: object = orjson.loads(native_response) - chains: object = response_payload.get("chains") if isinstance(response_payload, dict) else None - if not isinstance(chains, list) or len(chains) != len(tests): - raise NativeSqlTestPlanningError( - "native SQL-test chain resolution returned an invalid response" - ) - return tuple(_string_tuple(value=chain, context="chain") for chain in chains) + return tuple(tuple(chain) for chain in native_chains) def _planning_error(*, error: ValueError, tests: tuple[CompiledSqlTest, ...]) -> Exception: @@ -450,129 +326,6 @@ def _unresolved_reference_error( ) -def _plan_from_payload(*, value: object) -> NativeSqlTestPlan: - payload: dict[str, Any] = _object(value=value, context="result") - sql: object = payload.get("sql") - chain: object = payload.get("chain") - assertions: object = payload.get("assertions") - warnings: object = payload.get("warnings") - if ( - (sql is not None and not isinstance(sql, str)) - or not isinstance(chain, list) - or not isinstance(assertions, list) - or not isinstance(warnings, list) - ): - raise NativeSqlTestPlanningError("native SQL-test planning returned an invalid result") - plan_warnings: tuple[PlanWarning, ...] = tuple(_warning(value=warning) for warning in warnings) - return NativeSqlTestPlan( - chain=tuple(_chain_step(value=step) for step in chain), - assertions=tuple(_assertion_step(value=step) for step in assertions), - model_names=_string_tuple(value=payload.get("modelNames"), context="model names"), - warnings=plan_warnings, - error_messages=sql_test_plan_error_messages(warnings=plan_warnings), - sql=sql if isinstance(sql, str) else None, - ) - - -def _chain_step(*, value: object) -> ChainStep: - payload: dict[str, Any] = _object(value=value, context="chain step") - return ChainStep( - model_name=_string(value=payload.get("modelName"), context="chain step model"), - resolved_sql=_string(value=payload.get("resolvedSql"), context="chain step SQL"), - expected_cte_sql=_optional_string( - value=payload.get("expectedCteSql"), context="chain step expected SQL" - ), - lifted_ctes=_cte_pairs(value=payload.get("liftedCtes")), - comparison_body_sql=_optional_string( - value=payload.get("comparisonBodySql"), context="chain step comparison SQL" - ), - expected_columns=_optional_string_tuple( - value=payload.get("expectedColumns"), context="chain step expected columns" - ), - expected_lifted_ctes=_cte_pairs(value=payload.get("expectedLiftedCtes", [])), - ) - - -def _assertion_step(*, value: object) -> SqlTestAssertionStep: - payload: dict[str, Any] = _object(value=value, context="assertion step") - return SqlTestAssertionStep( - name=_string(value=payload.get("name"), context="assertion name"), - resolved_sql=_string(value=payload.get("resolvedSql"), context="assertion SQL"), - lifted_ctes=_cte_pairs(value=payload.get("liftedCtes")), - comparison_body_sql=_optional_string( - value=payload.get("comparisonBodySql"), context="assertion comparison SQL" - ), - ) - - -def _warning(*, value: object) -> PlanWarning: - payload: dict[str, Any] = _object(value=value, context="warning") - return PlanWarning( - model_name=_optional_string(value=payload.get("modelName"), context="warning model"), - severity=WarningSeverity( - _string(value=payload.get("severity"), context="warning severity") - ), - message=_string(value=payload.get("message"), context="warning message"), - ) - - -def _cte_pairs(*, value: object) -> tuple[tuple[str, str], ...]: - if not isinstance(value, list): - raise NativeSqlTestPlanningError("native SQL-test planning returned invalid lifted CTEs") - pairs: list[tuple[str, str]] = [] - for pair in value: - name, sql = _string_tuple(value=pair, context="lifted CTE") - pairs.append((name, sql)) - return tuple(pairs) - - -def _object(*, value: object, context: str) -> dict[str, Any]: - if not isinstance(value, dict): - raise NativeSqlTestPlanningError(f"native SQL-test planning returned an invalid {context}") - return cast(dict[str, Any], value) - - -def _string(*, value: object, context: str) -> str: - if not isinstance(value, str): - raise NativeSqlTestPlanningError(f"native SQL-test planning returned an invalid {context}") - return value - - -def _optional_string(*, value: object, context: str) -> str | None: - return None if value is None else _string(value=value, context=context) - - -def _optional_string_tuple(*, value: object, context: str) -> tuple[str, ...] | None: - return None if value is None else _string_tuple(value=value, context=context) - - -def _string_tuple(*, value: object, context: str) -> tuple[str, ...]: - if not isinstance(value, list): - raise NativeSqlTestPlanningError(f"native SQL-test planning returned an invalid {context}") - return tuple(_string(value=item, context=context) for item in value) - - -def _function_requests( - *, project: CompiledProject, adapter: BaseAdapter -) -> list[dict[str, object]]: - return [ - { - "name": name, - "udfPrefix": udf_prefix, - "udfSuffix": udf_suffix, - "tableFunctionPrefix": table_function_prefix, - "tableFunctionSuffix": table_function_suffix, - } - for ( - name, - udf_prefix, - udf_suffix, - table_function_prefix, - table_function_suffix, - ) in _function_templates(project=project, adapter=adapter) - ] - - def _function_templates( *, project: CompiledProject, adapter: BaseAdapter ) -> tuple[tuple[str, str, str, str, str], ...]: @@ -601,40 +354,6 @@ def _function_templates( return tuple(functions) -def _planning_test_requests( - *, project: CompiledProject, tests: tuple[CompiledSqlTest, ...], adapter: BaseAdapter -) -> list[dict[str, object]]: - """Build test requests whose model SQL has cursor intrinsics rendered for the test window.""" - - models_by_name: dict[str, CompiledModel] = {model.name: model for model in project.models} - windowed_indexes: tuple[int, ...] = tuple( - index - for index, test in enumerate(tests) - if isinstance(test.payload, CompiledModelSqlTestPayload) - and declares_cursor_window(test=test) - ) - chains: tuple[tuple[str, ...], ...] = ( - resolve_sql_test_model_chains( - project=project, tests=tuple(tests[index] for index in windowed_indexes) - ) - if windowed_indexes - else () - ) - chains_by_index: dict[int, tuple[str, ...]] = dict(zip(windowed_indexes, chains, strict=True)) - return [ - _test_request( - test=test, - model_query_overrides=_windowed_model_query_overrides( - test=test, - models_by_name=models_by_name, - adapter=adapter, - chain=chains_by_index.get(index), - ), - ) - for index, test in enumerate(tests) - ] - - def _windowed_model_query_overrides( *, test: CompiledSqlTest, @@ -671,87 +390,8 @@ def _windowed_model_query_overrides( return overrides -def _test_request( - *, test: CompiledSqlTest, model_query_overrides: dict[str, str] | None = None -) -> dict[str, object]: - payload: CompiledModelSqlTestPayload | CompiledDirectLogicSqlTestPayload = test.payload - if isinstance(payload, CompiledDirectLogicSqlTestPayload): - payload_request: dict[str, object] = { - "kind": "direct", - "mode": payload.mode.value, - "actualCte": _cte_request(cte=payload.actual_cte), - "expectedCte": _cte_request(cte=payload.expected_cte), - "helperCtes": [_cte_request(cte=cte) for cte in payload.helper_ctes], - } - else: - payload_request = { - "kind": "model", - "authoredCtes": [_cte_request(cte=cte) for cte in payload.authored_ctes], - "modelQueryOverrides": ( - payload.model_query_overrides - if model_query_overrides is None - else model_query_overrides - ), - "expectedCtes": [_cte_request(cte=cte) for cte in payload.expected_ctes], - "expectedModelNames": list(payload.expected_model_names), - "assertionCtes": [_cte_request(cte=cte) for cte in payload.assertion_ctes], - "readHelperNames": list(test.read_helper_names), - "referenceTargetModelNames": list(test.reference_target_model_names), - } - return { - "name": test.name, - "fileLabel": str(test.test_file.relative_path), - "payload": payload_request, - } - - -def _model_requests(*, project: CompiledProject, adapter: BaseAdapter) -> list[dict[str, object]]: - return [ - { - "name": model.name, - "querySql": render_test_cursor_intrinsics( - sql=model.query_sql, model=model, adapter=adapter, test=None - ), - "modelDependencies": _model_dependencies(model=model), - } - for model in project.models - ] - - -def _chain_model_requests(*, project: CompiledProject) -> list[dict[str, object]]: - """Chain ordering reads only declared dependencies, so model SQL is not sent.""" - - return [ - {"name": model.name, "modelDependencies": _model_dependencies(model=model)} - for model in project.models - ] - - -def _model_dependencies(*, model: CompiledModel) -> list[str]: - return [ - dependency.name - for dependency in model.deps - if dependency.resource_type == CompiledResourceType.MODEL - ] - - -def _cte_request(*, cte: CompileSqlTestCte) -> dict[str, str]: - return {"name": cte.name, "sqlBody": cte.sql_body} - - def _render_call_template(*, rendered: str) -> tuple[str, str]: prefix, separator, suffix = rendered.partition(_CALL_SUFFIX_SENTINEL) if not separator or _CALL_SUFFIX_SENTINEL in suffix: raise NativeSqlTestPlanningError("adapter SQL function call template is not deterministic") return prefix, suffix - - -def _lexical_syntax_request(syntax: SqlLexicalSyntax) -> dict[str, object]: - return { - "backslashEscapeQuotes": sorted(syntax.backslash_escape_quotes), - "escapeStringPrefix": syntax.escape_string_prefix, - "rawStringPrefix": syntax.raw_string_prefix, - "tripleQuotedStrings": syntax.triple_quoted_strings, - "nestedBlockComments": syntax.nested_block_comments, - "lineCommentPrefixes": sorted(syntax.line_comment_prefixes), - } diff --git a/src/sqlbuild/compiler/planner/main/execution/sql_test_artifacts.py b/src/sqlbuild/compiler/planner/main/execution/sql_test_artifacts.py deleted file mode 100644 index 8eaffd9f5c..0000000000 --- a/src/sqlbuild/compiler/planner/main/execution/sql_test_artifacts.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Public planner entrypoint for compiled SQL-test artifacts.""" - -from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter -from sqlbuild.compiler.compile.models import CompiledProject, CompiledSqlTest -from sqlbuild.compiler.planner._helpers.sql_tests.native_planning import ( - plan_and_render_sql_test_artifacts as _plan_and_render_sql_test_artifacts, -) -from sqlbuild.compiler.planner.models import NativeSqlTestArtifact - - -def plan_and_render_sql_test_artifacts( - *, - project: CompiledProject, - tests: tuple[CompiledSqlTest, ...], - adapter: BaseAdapter, - sql_analysis_enabled: bool, -) -> tuple[NativeSqlTestArtifact, ...]: - """Plan and render SQL tests in one deterministic native project call.""" - - return _plan_and_render_sql_test_artifacts( - project=project, - tests=tests, - adapter=adapter, - sql_analysis_enabled=sql_analysis_enabled, - ) diff --git a/src/sqlbuild/executor/testing/types.py b/src/sqlbuild/executor/testing/types.py index 3a1d96b2e7..f26fb0dea4 100644 --- a/src/sqlbuild/executor/testing/types.py +++ b/src/sqlbuild/executor/testing/types.py @@ -11,10 +11,6 @@ class NativeSqlTestRenderingModule(Protocol): def render_sql_test_comparisons_json(self, request_json: str) -> str: ... - def plan_and_render_sql_tests_json(self, request_json: str) -> str: ... - - def resolve_sql_test_chains_json(self, request_json: str) -> str: ... - def render_sql_test_difference_sample_json(self, request_json: str) -> str: ... From 2ca9ac2ee2917b3856a2fc8b49a36b04ebe5b0b9 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 14:58:20 +0100 Subject: [PATCH 02/36] test(compiler): drop python-engine parity tests and keep their coverage on native Tests that compared the removed python engine with native now either compare native with native-preview, keeping every explicit expectation, or compare native with the Python fallback it still defers to (project assembly, SQL-test assembly, contracts, semantic completion, type normalisation). Pure parity tests whose Python side was deleted are removed; goldens and the corpus pin that output. The Rust JSON planning entry points become test-only helpers now that their bindings are gone. --- .../compiler/_helpers/sql_tests/planning.rs | 4 + .../src/compiler/main/mod.rs | 2 - .../main/sql_test_chain_resolution.rs | 5 - .../src/compiler/main/sql_test_planning.rs | 5 - .../src/compiler/tests/helpers.rs | 16 +- .../compiler/tests/test_sql_test_planning.rs | 4 +- ...ge_enabled.py => _native_stage_enabled.py} | 0 ...ive_answer.py => _report_native_answer.py} | 0 tests/conftest.py | 10 +- .../test_compare_compile_performance.py | 6 +- .../scripts/compiler_differential/helpers.py | 22 +- .../test_compiler_differential.py | 22 +- .../test_engine_error_cases.py | 2 +- .../test_failure_corpus_codes.py | 4 +- .../test_golden_outputs.py | 10 +- .../test_native_fallback_gate.py | 89 +-- .../test_reference_differential.py | 2 +- .../test_scope_differential.py | 13 +- .../main/build/test_model_config_engines.py | 28 +- .../cli/commands/main/compile/_test_types.py | 71 +- .../compile/attachment_errors/_test_types.py | 4 +- .../test_native_attachment_errors.py | 28 +- .../main/compile/fast_lineage/_test_types.py | 1 - .../fast_lineage/test_native_fast_lineage.py | 22 +- .../cli/commands/main/compile/helpers.py | 40 +- .../test_native_semantic_checks_cli.py | 15 +- .../test_native_sql_test_errors.py | 8 +- .../compile/test_compile_macro_call_store.py | 25 +- .../compile/test_compiler_engine_switch.py | 236 ------ .../compile/test_compiler_engine_tiers.py | 10 +- .../main/compile/test_deep_header_nesting.py | 20 +- .../test_dollar_quoted_project_vars.py | 2 +- .../compile/test_native_analysis_seams.py | 18 +- .../main/compile/test_native_config_errors.py | 25 +- .../compile/test_native_declaration_errors.py | 8 +- .../main/compile/test_native_macro_errors.py | 6 +- .../main/compile/test_native_model_config.py | 129 ---- .../main/compile/test_native_model_loop.py | 106 --- .../compile/test_native_reference_errors.py | 42 +- .../main/compile/test_native_type_system.py | 22 +- .../compile/test_reference_call_syntax.py | 77 -- .../main/test/test_direct_logic_extraction.py | 63 -- .../main/test/test_helper_references.py | 312 -------- .../sqlbuild/adapter/type_system/helpers.py | 21 +- .../type_system/test_native_type_parity.py | 10 +- .../compiler/attachments/_test_types.py | 86 --- .../sqlbuild/compiler/attachments/helpers.py | 702 ------------------ .../test_native_attachment_project.py | 249 ------- .../attachments/test_native_authored_sql.py | 71 -- .../attachments/test_native_omitted_select.py | 82 -- .../test_native_raw_direct_logic_tests.py | 70 -- .../test_native_scenario_extraction.py | 84 --- .../test_native_test_parameters.py | 58 -- .../attachments/test_native_test_targets.py | 69 -- .../sqlbuild/compiler/compile/_test_types.py | 1 - .../compile/test_native_macro_bridge.py | 35 +- .../compiler/contracts/_test_types.py | 10 - .../sqlbuild/compiler/contracts/helpers.py | 66 +- .../contracts/test_native_model_contracts.py | 46 -- .../compiler/discovery/_test_types.py | 9 - .../sqlbuild/compiler/discovery/helpers.py | 6 +- .../discovery/test_deep_header_values.py | 2 +- .../discovery/test_discovery_failure_text.py | 10 +- .../test_native_declaration_discovery.py | 72 -- .../test_native_declaration_files.py | 23 +- .../discovery/test_native_model_discovery.py | 33 - .../test_native_sql_test_discovery.py | 66 -- .../test_native_yaml_file_discovery.py | 47 -- .../model_config/test_native_model_config.py | 16 +- .../sqlbuild/compiler/model_loop/__init__.py | 0 .../compiler/model_loop/_test_types.py | 1 - .../sqlbuild/compiler/model_loop/helpers.py | 175 ----- .../test_batched_analysis_preparation.py | 2 +- .../test_dataflow_analysis_schedule.py | 2 +- .../pipeline/test_expression_catalog_cache.py | 2 +- .../pipeline/test_shared_binding_queries.py | 2 +- .../compiler/project_assembly/helpers.py | 34 +- .../test_native_project_assembly.py | 29 +- .../src/sqlbuild/compiler/scopes/helpers.py | 16 +- .../scopes/test_native_scope_parity.py | 27 +- .../compiler/semantic_checks/helpers.py | 25 +- .../test_native_metadata_checks.py | 2 +- .../compiler/sql_test_glue/_test_types.py | 19 - .../compiler/sql_test_glue/helpers.py | 192 +---- .../test_native_sql_test_assembly.py | 84 +++ .../test_native_sql_test_planning_glue.py | 237 ------ .../_helpers/goldens/test_compiler_goldens.py | 28 +- .../_helpers/running/test_evidence.py | 16 +- .../_helpers/running/test_native_fallbacks.py | 41 +- .../_helpers/running/test_options.py | 8 +- .../_helpers/running/test_records.py | 8 +- .../_helpers/running/test_report.py | 2 +- .../entry/test_compiler_engine_option.py | 27 +- .../test_compiler_engine_reuse_key.py | 14 +- .../compile/_helpers/config/helpers.py | 5 +- .../compile/_helpers/test_analysis_cache.py | 2 +- .../sqlbuild/compiler/frontier/_test_types.py | 30 - .../compiler/frontier/test_cache_namespace.py | 18 +- .../frontier/test_native_stage_tiers.py | 408 ---------- .../frontier/test_resolve_compiler_engine.py | 30 +- .../_helpers/sql_test_assembly/helpers.py | 4 +- .../sql_test_assembly/test_plan_test.py | 6 +- 102 files changed, 542 insertions(+), 4432 deletions(-) delete mode 100644 crates/sqlbuild-analysis/src/compiler/main/sql_test_chain_resolution.rs delete mode 100644 crates/sqlbuild-analysis/src/compiler/main/sql_test_planning.rs rename src/sqlbuild/compiler/frontier/main/{native_stage_enabled.py => _native_stage_enabled.py} (100%) rename src/sqlbuild/compiler/frontier/main/{report_native_answer.py => _report_native_answer.py} (100%) delete mode 100644 tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_switch.py delete mode 100644 tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_config.py delete mode 100644 tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_loop.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_attachment_project.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_authored_sql.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_omitted_select.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_raw_direct_logic_tests.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_scenario_extraction.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_test_parameters.py delete mode 100644 tests/integration/src/sqlbuild/compiler/attachments/test_native_test_targets.py delete mode 100644 tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_discovery.py delete mode 100644 tests/integration/src/sqlbuild/compiler/discovery/test_native_sql_test_discovery.py delete mode 100644 tests/integration/src/sqlbuild/compiler/model_loop/__init__.py delete mode 100644 tests/integration/src/sqlbuild/compiler/model_loop/_test_types.py delete mode 100644 tests/integration/src/sqlbuild/compiler/model_loop/helpers.py create mode 100644 tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py delete mode 100644 tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py delete mode 100644 tests/unit/src/sqlbuild/compiler/frontier/test_native_stage_tiers.py diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs index e879c9da90..dd807f1445 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs @@ -7,6 +7,7 @@ use std::time::Instant; use polyglot_sql::{Dialect, Expression}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use regex::Regex; +#[cfg(test)] use serde::Serialize; use crate::compiler::_helpers::sql_tests::cte_rename::defined_cte_keys; @@ -65,6 +66,7 @@ pub(crate) const DBT_REF_FUNCTION: &str = "__dbt_ref"; const UDF_FUNCTION: &str = "__udf"; const TABLE_FUNCTION: &str = "__table_fn"; +#[cfg(test)] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct ChainBatchResponse { @@ -426,6 +428,7 @@ impl GeneratedCteState { } /// Return each test's topologically ordered unmocked model chain without planning SQL. +#[cfg(test)] pub(crate) fn resolve_chains_json(request_json: &str) -> Result { let request: SqlTestChainBatch = serde_json::from_str(request_json).map_err(|error| error.to_string())?; @@ -496,6 +499,7 @@ fn chain_root_names( dedupe(expected_names) } +#[cfg(test)] pub(crate) fn plan_and_render_json(request_json: &str) -> Result { let request: SqlTestPlanBatch = serde_json::from_str(request_json).map_err(|error| error.to_string())?; diff --git a/crates/sqlbuild-analysis/src/compiler/main/mod.rs b/crates/sqlbuild-analysis/src/compiler/main/mod.rs index bb4c16b14f..8f17d7b413 100644 --- a/crates/sqlbuild-analysis/src/compiler/main/mod.rs +++ b/crates/sqlbuild-analysis/src/compiler/main/mod.rs @@ -1,12 +1,10 @@ pub mod function_type_validation; pub mod sql_scenario_extraction; pub mod sql_test_assembly; -pub mod sql_test_chain_resolution; pub mod sql_test_chains; pub mod sql_test_difference_sampling; pub mod sql_test_extraction; pub mod sql_test_fixture_facts; pub mod sql_test_glue; pub mod sql_test_plan_errors; -pub mod sql_test_planning; pub mod sql_test_rendering; diff --git a/crates/sqlbuild-analysis/src/compiler/main/sql_test_chain_resolution.rs b/crates/sqlbuild-analysis/src/compiler/main/sql_test_chain_resolution.rs deleted file mode 100644 index 74ae93cc0d..0000000000 --- a/crates/sqlbuild-analysis/src/compiler/main/sql_test_chain_resolution.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::compiler::_helpers; - -pub fn resolve_chains_json(request_json: &str) -> Result { - _helpers::sql_tests::planning::resolve_chains_json(request_json) -} diff --git a/crates/sqlbuild-analysis/src/compiler/main/sql_test_planning.rs b/crates/sqlbuild-analysis/src/compiler/main/sql_test_planning.rs deleted file mode 100644 index d5057b8241..0000000000 --- a/crates/sqlbuild-analysis/src/compiler/main/sql_test_planning.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::compiler::_helpers; - -pub fn plan_and_render_json(request_json: &str) -> Result { - _helpers::sql_tests::planning::plan_and_render_json(request_json) -} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs b/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs index b53085d4bd..99ffa40a63 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs @@ -1864,7 +1864,7 @@ pub(crate) fn plan_helper_reference_response( case: &HelperReferenceTestCase, sends_compiler_reads: bool, ) -> Result { - crate::compiler::main::sql_test_planning::plan_and_render_json( + crate::compiler::_helpers::sql_tests::planning::plan_and_render_json( &json!({ "lexicalSyntax": generic_lexical_syntax(), "models": helper_reference_models(), @@ -1889,7 +1889,7 @@ pub(crate) fn plan_helper_reference_case(case: &HelperReferenceTestCase) -> Valu /// Resolve one helper-reference case's model chain without planning SQL. pub(crate) fn chain_helper_reference_case(case: &HelperReferenceTestCase) -> Value { let response: Value = serde_json::from_str( - &crate::compiler::main::sql_test_chain_resolution::resolve_chains_json( + &crate::compiler::_helpers::sql_tests::planning::resolve_chains_json( &json!({ "lexicalSyntax": generic_lexical_syntax(), "models": helper_reference_models(), @@ -1910,16 +1910,16 @@ pub(crate) fn defined_before(sql: &str, first: &str, second: &str) -> bool { /// Plan a request whose model tests predate the compiler's reads, sending empty reads for them. pub(crate) fn plan_json(request_json: &str) -> Result { - crate::compiler::main::sql_test_planning::plan_and_render_json(&with_empty_compiler_reads( - request_json, - )) + crate::compiler::_helpers::sql_tests::planning::plan_and_render_json( + &with_empty_compiler_reads(request_json), + ) } /// Resolve chains for a request whose model tests predate the compiler's reads. pub(crate) fn chain_json(request_json: &str) -> Result { - crate::compiler::main::sql_test_chain_resolution::resolve_chains_json( - &with_empty_compiler_reads(request_json), - ) + crate::compiler::_helpers::sql_tests::planning::resolve_chains_json(&with_empty_compiler_reads( + request_json, + )) } fn with_empty_compiler_reads(request_json: &str) -> String { diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs index a54b22b9de..f7e25c954a 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs @@ -1,10 +1,10 @@ use serde_json::{Value, json}; -use crate::compiler::main::sql_test_chain_resolution::resolve_chains_json; +use crate::compiler::_helpers::sql_tests::planning::plan_and_render_json; +use crate::compiler::_helpers::sql_tests::planning::resolve_chains_json; use crate::compiler::main::sql_test_chains::resolve_sql_test_chains; use crate::compiler::main::sql_test_glue::plan_sql_test_batch; use crate::compiler::main::sql_test_plan_errors::sql_test_plan_error_messages; -use crate::compiler::main::sql_test_planning::plan_and_render_json; use crate::compiler::models::{SqlTestPlan, SqlTestPlanWarning}; use crate::compiler::tests::helpers::{ chain_resolution_orders_unmocked_models, concurrent_requests_initialize_shared_template_once, diff --git a/src/sqlbuild/compiler/frontier/main/native_stage_enabled.py b/src/sqlbuild/compiler/frontier/main/_native_stage_enabled.py similarity index 100% rename from src/sqlbuild/compiler/frontier/main/native_stage_enabled.py rename to src/sqlbuild/compiler/frontier/main/_native_stage_enabled.py diff --git a/src/sqlbuild/compiler/frontier/main/report_native_answer.py b/src/sqlbuild/compiler/frontier/main/_report_native_answer.py similarity index 100% rename from src/sqlbuild/compiler/frontier/main/report_native_answer.py rename to src/sqlbuild/compiler/frontier/main/_report_native_answer.py diff --git a/tests/conftest.py b/tests/conftest.py index ad1fd020f1..b0fd51dc67 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ import pytest from sqlbuild.cli.compile_reuse.constants import REUSE_DISABLE_ENV_VAR, REUSE_DISABLE_VALUE -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR +from sqlbuild.compiler.compile._helpers.native_stages import assembly as native_assembly def pytest_configure(config: pytest.Config) -> None: @@ -18,7 +18,9 @@ def pytest_configure(config: pytest.Config) -> None: @pytest.fixture -def python_compiler_engine(monkeypatch: pytest.MonkeyPatch) -> None: - """Compile with the Python engine, for tests observing its analysis and planning internals.""" +def deferred_native_analysis(monkeypatch: pytest.MonkeyPatch) -> None: + """Make native model analysis and expression-source shapes defer, so compiles run the Python + analysis that answers native deferrals.""" - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, "python") + monkeypatch.setattr(native_assembly, "analyze_native_model_sql", lambda **_: None) + monkeypatch.setattr(native_assembly, "infer_native_expression_source_shapes", lambda **_: None) diff --git a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py index 4faf2e6433..36d9e53d8e 100644 --- a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py +++ b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py @@ -173,12 +173,12 @@ def test_given_base_root_when_comparing_compile_performance_then_each_side_uses_ "test_case", ( EnginePhaseRatioTestCase( - description="native-preview contracts within a generous limit of python's", + description="native-preview contracts within a generous limit of native's", max_ratio="1000", phase_noise_floor_ms="1000", expected_return_code=0, expected_fragments=( - "Engines: base `python`, head `native-preview`.", + "Engines: base `native`, head `native-preview`.", "Gated phases: contracts_cpu_ms.", "| contracts_cpu_ms |", "**Result: passed.**", @@ -245,7 +245,7 @@ def test_given_two_engines_when_gating_a_phase_then_enforces_only_that_phase( "--head-python", sys.executable, "--base-engine", - "python", + "native", "--head-engine", "native-preview", "--gate-phase", diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index f58bd67b3c..2110dc67b6 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -111,24 +111,6 @@ def _perturbed(self, **changes): native.start_model_analysis_session = lambda *_args: None """ -_STAGE_DISABLE_SABOTAGE: str = """ -import sqlbuild.compiler.frontier.main.native_stage_enabled as enabled -from sqlbuild.compiler.frontier.types import NativeStage - -_original = enabled.native_stage_enabled -_disabled = {{{stages}}} -enabled.native_stage_enabled = lambda stage: stage not in _disabled and _original(stage) -""" - - -def stage_disable_sabotage(stages: tuple[str, ...]) -> str: - """A perturbation switching these native stages off, so their work runs in Python.""" - - return _STAGE_DISABLE_SABOTAGE.format( - stages=", ".join(f"NativeStage({stage!r})" for stage in stages) - ) - - _BROKEN_REF_FILES: dict[str, str] = { "sqlbuild_project.toml": ( 'name = "broken_orders"\nadapter = "duckdb"\n\n' @@ -214,7 +196,7 @@ def shared_analysis_seed_arguments(*, work_dir: Path, seed: int) -> list[str]: "--seeds", "1", "--engines", - "python", - "python", + "native", + "native", "--stage-captures", ] diff --git a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py index 72e273cd74..4d95fa8795 100644 --- a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py +++ b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py @@ -47,7 +47,7 @@ expected_exit_code=0, expected_lines=( "OK project/waffle_shop", - "Compiler differential passed: 1 projects identical (python vs native-preview)", + "Compiler differential passed: 1 projects identical (native vs native-preview)", ), expected_patterns=(), expected_absent=("DIFF",), @@ -346,9 +346,9 @@ def test_given_required_coverage_missing_when_comparing_then_harness_fails_and_s seed=SHARED_ANALYSIS_SEED, expected_lines=( f"OK seed/{SHARED_ANALYSIS_SEED}", - "Compiler differential passed: 3 projects identical (python vs python)", + "Compiler differential passed: 3 projects identical (native vs native)", ), - expected_capture_sides=("left-python-captures", "right-python-captures"), + expected_capture_sides=("left-native-captures", "right-native-captures"), expected_compile_exit_code=0, expected_minimum_shareable_members=1, expected_minimum_shared_reuse=1, @@ -356,8 +356,8 @@ def test_given_required_coverage_missing_when_comparing_then_harness_fails_and_s ], ids=lambda case: case.description, ) -@pytest.mark.usefixtures("python_compiler_engine") -def test_given_python_engine_twice_when_capturing_stages_then_captures_are_identical( +@pytest.mark.usefixtures("deferred_native_analysis") +def test_given_native_engine_twice_when_capturing_stages_then_captures_are_identical( test_case: SharedAnalysisSeedTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str], @@ -437,12 +437,12 @@ def test_given_failure_expectation_when_code_is_not_the_first_error_then_harness "test_case", [ WheelSiteReportTestCase( - description="python_and_preview_wheel_calls_without_preview_deferrals", + description="native_and_preview_wheel_calls_without_preview_deferrals", perturbation="", expected_lines=( - "Polyglot wheel calls (python):", + "Polyglot wheel calls (native):", "Polyglot wheel calls (native-preview):", - "Analysis deferrals (python): none recorded", + "Analysis deferrals (native): none recorded", "Analysis deferrals (native-preview): none recorded", "Compiler differential passed: 1 projects identical", ), @@ -458,7 +458,7 @@ def test_given_failure_expectation_when_code_is_not_the_first_error_then_harness description="preview_deferrals_are_counted_per_kind_and_site", perturbation=DEFERRAL_PERTURBATION, expected_lines=( - "Analysis deferrals (python): none recorded", + "Analysis deferrals (native): none recorded", "Analysis deferrals (native-preview):", " legacy_fallback orders.sql (project ", ), @@ -490,10 +490,10 @@ def test_given_wheel_site_report_when_comparing_then_sites_and_deferrals_are_rep preview: dict[str, dict[str, dict[str, int]]] = report["native-preview"] assert exit_code == 0, output assert all(line in output for line in test_case.expected_lines), output - assert test_case.expected_sites <= set(report["python"]["wheel_sites"]), report + assert test_case.expected_sites <= set(report["native"]["wheel_sites"]), report assert set(preview["deferrals"]) == test_case.expected_deferrals assert all(sum(by_corpus.values()) > 0 for by_corpus in preview["deferrals"].values()) - assert report["python"]["deferrals"] == {} + assert report["native"]["deferrals"] == {} if __name__ == "__main__": diff --git a/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py b/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py index 6cd931ccf1..0ba94842af 100644 --- a/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py +++ b/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py @@ -15,7 +15,7 @@ from scripts.compiler_differential.models import DifferentialOptions, EmittedCodes from tests.e2e.scripts.compiler_differential._test_types import EngineErrorCaseTestCase -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") @pytest.mark.parametrize( diff --git a/tests/e2e/scripts/compiler_differential/test_failure_corpus_codes.py b/tests/e2e/scripts/compiler_differential/test_failure_corpus_codes.py index 172a53588d..cb3524f5f4 100644 --- a/tests/e2e/scripts/compiler_differential/test_failure_corpus_codes.py +++ b/tests/e2e/scripts/compiler_differential/test_failure_corpus_codes.py @@ -32,7 +32,7 @@ "test_case", [ FailureCorpusCodesTestCase( - description="python_engine", + description="native_engine", expected_first_errors={ f"failure/{case.name}": case.expected_code for case in failure_cases() }, @@ -82,7 +82,7 @@ def test_given_failure_corpus_when_compiling_then_first_errors_match_and_cover_e ) -> None: emitted: dict[str, EmittedCodes] = emitted_failure_codes( options=DifferentialOptions( - engines=("python", "python"), + engines=("native", "native"), work_dir=tmp_path, jobs=4, stage_captures=False, diff --git a/tests/e2e/scripts/compiler_differential/test_golden_outputs.py b/tests/e2e/scripts/compiler_differential/test_golden_outputs.py index 493038863e..e19a229912 100644 --- a/tests/e2e/scripts/compiler_differential/test_golden_outputs.py +++ b/tests/e2e/scripts/compiler_differential/test_golden_outputs.py @@ -15,7 +15,13 @@ _CASE: str = "engine-error-week-date-cursor-start" _PROJECT: str = "orders" -_ENGINE_ARGUMENTS: tuple[str, ...] = ("--engines", "python", "native", "--expect", "failure:P001") +_ENGINE_ARGUMENTS: tuple[str, ...] = ( + "--engines", + "native", + "native-preview", + "--expect", + "failure:P001", +) @pytest.mark.parametrize( @@ -32,8 +38,8 @@ golden_edit=("hour must be in 0..23", "hour must be in 1..24"), expected_exit_code=1, expected_lines=( - " - golden (python) at /commands/0/diagnostics/0/message", " - golden (native) at /commands/0/diagnostics/0/message", + " - golden (native-preview) at /commands/0/diagnostics/0/message", "Compiler differential FAILED: 1 of 1 projects differ", ), ), diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 8899688f5d..10e2ebbe1e 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -1,4 +1,4 @@ -"""The fallback allow-list gate fails on sabotaged native stages and on stale entries.""" +"""The fallback allow-list gate fails on sabotaged native work and on stale entries.""" from __future__ import annotations @@ -12,23 +12,9 @@ MACRO_RESOLUTION_SABOTAGE, MODEL_ANALYSIS_SABOTAGE, harness_arguments, - stage_disable_sabotage, write_native_perturbation, ) -_WAFFLE_SHOP_NATIVE_STAGES: tuple[str, ...] = ( - "attachments", - "contracts", - "lineage_facts", - "macro_call_store", - "macro_calls", - "model_analysis", - "semantic_checks", -) -_ANSWER_VANISHED: str = "native no longer answers here, so this work now runs in Python" -_FALLBACK_WITHOUT_ANSWERS: str = ( - "fallback disappeared but native answers did not appear; the stage may be switched off" -) _VANISHED_ENTRY: str = ( '\n[[entry]]\nengine = "native-preview"\nstage = "macro_calls"\n' 'site = "macro_calls.resolution"\nkind = "deferred"\ncounts = { project = 1 }\n' @@ -46,13 +32,14 @@ expected_lines=("Compiler differential passed: 1 projects identical",), ), NativeFallbackGateTestCase( - description="entry_that_no_longer_occurs_without_new_native_answers_fails", + description="entry_that_no_longer_occurs_fails", perturbation="", appended_entries=_VANISHED_ENTRY, expected_exit_code=1, expected_lines=( "Native fallback allow-list: native-preview macro_calls " - f"macro_calls.resolution deferred (project): {_FALLBACK_WITHOUT_ANSWERS}", + "macro_calls.resolution deferred (project): listed but no longer occurs; remove it " + "from the allow-list", ), ), NativeFallbackGateTestCase( @@ -77,29 +64,6 @@ "Compiler differential FAILED: 0 of 1 projects differ", ), ), - NativeFallbackGateTestCase( - description="shipped_stages_switched_to_python_fail", - perturbation=stage_disable_sabotage(("attachments", "macro_calls")), - appended_entries="", - expected_exit_code=1, - expected_lines=( - "Native fallback allow-list: native-preview attachments " - f"attachments.native seed_pairs (project): {_ANSWER_VANISHED}", - "Native fallback allow-list: native-preview macro_calls " - f"macro_calls.native bridged_calls (project): {_ANSWER_VANISHED}", - "Intended? Run `make compiler-baselines`", - ), - ), - NativeFallbackGateTestCase( - description="every_native_stage_switched_to_python_fails_by_name", - perturbation=stage_disable_sabotage(_WAFFLE_SHOP_NATIVE_STAGES), - appended_entries="", - expected_exit_code=1, - expected_lines=tuple( - f"Native fallback allow-list: native-preview {stage} {stage}.native " - for stage in _WAFFLE_SHOP_NATIVE_STAGES - ), - ), ], ids=lambda case: case.description, ) @@ -139,50 +103,5 @@ def test_given_recorded_allow_list_when_native_defers_more_or_less_then_the_gate assert all(line in output for line in test_case.expected_lines), output -@pytest.mark.parametrize( - "test_case", - [ - NativeFallbackGateTestCase( - description="sql_test_glue_switched_off_is_not_a_finished_port", - perturbation=stage_disable_sabotage(("sql_test_glue",)), - appended_entries="", - expected_exit_code=1, - expected_lines=( - "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.native " - f"sql_test_assemblies (fixture): {_ANSWER_VANISHED}", - "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.native " - f"sql_test_plans (fixture): {_ANSWER_VANISHED}", - "Native fallback allow-list: 2 problems", - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_committed_allow_list_when_fallback_only_stage_is_switched_off_then_gate_refuses( - test_case: NativeFallbackGateTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] -) -> None: - sabotage: Path = write_native_perturbation(tmp_path / "sabotage", source=test_case.perturbation) - _ = capsys.readouterr() - - exit_code: int = run_compiler_differential( - [ - "--corpus", - "fixtures", - "--jobs", - "2", - "--work-dir", - str(tmp_path / "check"), - "--native-fallbacks", - "check", - "--engine-env", - f"native-preview:PYTHONPATH={sabotage}", - ] - ) - - output: str = capsys.readouterr().out - assert exit_code == test_case.expected_exit_code, output - assert all(line in output for line in test_case.expected_lines), output - - if __name__ == "__main__": pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/scripts/compiler_differential/test_reference_differential.py b/tests/e2e/scripts/compiler_differential/test_reference_differential.py index fb4140add6..af0b4613e1 100644 --- a/tests/e2e/scripts/compiler_differential/test_reference_differential.py +++ b/tests/e2e/scripts/compiler_differential/test_reference_differential.py @@ -42,7 +42,7 @@ expected_exit_code=0, expected_lines=( "OK project/table_function_orders", - "Compiler differential passed: 1 projects identical (python vs native-preview)", + "Compiler differential passed: 1 projects identical (native vs native-preview)", ), expected_patterns=(), expected_absent=("DIFF",), diff --git a/tests/e2e/scripts/compiler_differential/test_scope_differential.py b/tests/e2e/scripts/compiler_differential/test_scope_differential.py index 5a3032b21d..a0f0bec182 100644 --- a/tests/e2e/scripts/compiler_differential/test_scope_differential.py +++ b/tests/e2e/scripts/compiler_differential/test_scope_differential.py @@ -53,18 +53,7 @@ expected_exit_code=0, expected_lines=( "OK project/scoped_orders", - "Compiler differential passed: 1 projects identical (python vs native-preview)", - ), - expected_patterns=(), - expected_absent=("DIFF",), - ), - HarnessRunTestCase( - description="shipped_native_stages_only", - extra_arguments=("--engines", "python", "native"), - expected_exit_code=0, - expected_lines=( - "OK project/scoped_orders", - "Compiler differential passed: 1 projects identical (python vs native)", + "Compiler differential passed: 1 projects identical (native vs native-preview)", ), expected_patterns=(), expected_absent=("DIFF",), diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_model_config_engines.py b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_model_config_engines.py index d3e3ae5bfe..0e09890c18 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_model_config_engines.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_model_config_engines.py @@ -87,30 +87,6 @@ @pytest.mark.parametrize( "test_case", [ - ModelConfigEnginesBuildE2ETestCase( - description="layered and templated config builds on python", - engine="python", - overrides={}, - expected_exit_code=0, - expected_output_fragments=_PASSING_FRAGMENTS, - expected_relations=_BUILT_RELATIONS, - ), - ModelConfigEnginesBuildE2ETestCase( - description="reversed cursor bounds are the Python error on python", - engine="python", - overrides=_REVERSED_CURSOR_BOUNDS, - expected_exit_code=1, - expected_output_fragments=_REVERSED_BOUNDS_FRAGMENTS, - expected_relations=(), - ), - ModelConfigEnginesBuildE2ETestCase( - description="a snapshot without a unique key is the Python error on python", - engine="python", - overrides=_SNAPSHOT_WITHOUT_KEY, - expected_exit_code=1, - expected_output_fragments=_SNAPSHOT_WITHOUT_KEY_FRAGMENTS, - expected_relations=(), - ), ModelConfigEnginesBuildE2ETestCase( description="layered and templated config builds on native", engine="native", @@ -120,7 +96,7 @@ expected_relations=_BUILT_RELATIONS, ), ModelConfigEnginesBuildE2ETestCase( - description="reversed cursor bounds are the Python error on native", + description="reversed cursor bounds are the expected error on native", engine="native", overrides=_REVERSED_CURSOR_BOUNDS, expected_exit_code=1, @@ -144,7 +120,7 @@ expected_relations=_BUILT_RELATIONS, ), ModelConfigEnginesBuildE2ETestCase( - description="reversed cursor bounds are the Python error on native-preview", + description="reversed cursor bounds are the expected error on native-preview", engine="native-preview", overrides=_REVERSED_CURSOR_BOUNDS, expected_exit_code=1, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py index b5e13f46ea..9099ad17ef 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py @@ -822,18 +822,6 @@ class CompilerEngineParityTestCase: expected_exit_codes: tuple[int, int] -@dataclass(frozen=True) -class DefaultEngineParityTestCase: - """The Python oracle and the unset default compiling copies of one project, with stores.""" - - description: str - oracle_engine: str - default_engine: str - expected_engines: tuple[str, str] - expected_exit_codes: tuple[int, int] - expected_macro_call_stores: tuple[tuple[str, ...], tuple[str, ...]] - - @dataclass(frozen=True) class NativeTypeSystemTestCase: """Compiles of one project by each engine whose contracts compare types or fail.""" @@ -852,39 +840,6 @@ class NativeAnalysisSeamTestCase: expected_native_returns: dict[str, list[object]] -@dataclass(frozen=True) -class CompilerEngineMacroParityTestCase: - """Two engines compiling separate copies of one macro-heavy project.""" - - description: str - files: dict[str, str] - expected_exit_codes: tuple[int, int] - expected_compiled_fragments: tuple[str, ...] - - -@dataclass(frozen=True) -class CompilerEngineStoreTestCase: - """A sequence of engine compiles in one project and which of them reuse a stored compile.""" - - description: str - engines: tuple[str, ...] - expected_reused: tuple[bool, ...] - expected_python_stores: tuple[str, ...] - expected_native_stores: tuple[str, ...] - - -@dataclass(frozen=True) -class CompilerEngineRulesStoreTestCase: - """Compile plus Rules under each engine and the store files each engine must own.""" - - description: str - rules_selector: str - stores: tuple[str, ...] - native_marker: str - expected_python_files: tuple[str, ...] - expected_native_files: tuple[str, ...] - - @dataclass(frozen=True) class CompilerEngineTierStoreTestCase: """Alternating compiles under two native tiers and the stores each tier must own alone.""" @@ -896,19 +851,6 @@ class CompilerEngineTierStoreTestCase: expected_second_engine_stores: tuple[str, ...] -@dataclass(frozen=True) -class NativeModelConfigParityTestCase: - """Authored model config compiled by two engines that must report and write the same.""" - - description: str - project_files: dict[str, str] - project_config_replacements: tuple[tuple[str, str], ...] - environment: dict[str, str] - engines: tuple[str, str] - expected_exit_codes: tuple[int, int] - expected_report_text: str - - @dataclass(frozen=True) class ReplaceableReferenceCallTestCase: """A compiler engine that must build double-quoted reference calls end to end.""" @@ -929,17 +871,6 @@ class RejectedReferenceCallTestCase: expected_diagnostics: tuple[tuple[str, str, str, int, int, str], ...] -@dataclass(frozen=True) -class NativeModelLoopParityTestCase: - """Models whose declarations two engines must resolve, report and write the same way.""" - - description: str - project_files: dict[str, str] - engines: tuple[str, str] - expected_exit_codes: tuple[int, int] - expected_report_text: str - - @dataclass(frozen=True) class NativeDeclarationErrorTestCase: """A failing project whose declaration error both engines must report the same way.""" @@ -957,7 +888,7 @@ class ReferenceScanErrorTestCase: description: str project_files: dict[str, str] expected_message: str - expected_macro_calls: tuple[int, int, int] + expected_macro_calls: tuple[int, int] expected_error_types: tuple[str, ...] diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/_test_types.py index 4b42c4ca3f..dbb25bfb0a 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/_test_types.py @@ -9,5 +9,5 @@ class NativeAttachmentErrorTestCase: description: str case_name: str - expected_macro_calls: tuple[int, int, int] - expected_error_types: tuple[str, str, str] + expected_macro_calls: tuple[int, int] + expected_error_types: tuple[str, str] diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/test_native_attachment_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/test_native_attachment_errors.py index 3bf28e5ad2..38e5b9a7f8 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/test_native_attachment_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/attachment_errors/test_native_attachment_errors.py @@ -24,7 +24,7 @@ write_project_file, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _COUNTED_MACRO: str = ( "from pathlib import Path\n\n\n" @@ -36,7 +36,7 @@ ' _ = log.write("call\\n")\n' " return expression\n" ) -_ERROR_TYPES: tuple[str, str, str] = ("CompileInputError", "CompileInputError", "CompileInputError") +_ERROR_TYPES: tuple[str, str] = ("CompileInputError", "CompileInputError") _CASES: dict[str, FailureCase] = {case.name: case for case in attachment_failure_cases()} @@ -46,55 +46,55 @@ NativeAttachmentErrorTestCase( description="an unknown audit run scope", case_name="generic-audit-unknown-run-scope", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="an audit argument overriding the implicit model", case_name="generic-audit-overrides-implicit-model", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="a cursor intrinsic in a generic audit reads expanded SQL", case_name="generic-audit-cursor-intrinsic", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="an unknown project variable in audit SQL", case_name="audit-unknown-project-variable", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="an unknown project variable inside doubled backticks in audit SQL", case_name="audit-variable-in-doubled-backticks", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="a SQL function return type the adapter dialect cannot parse", case_name="sql-function-invalid-return-type", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="a SQL function without returns", case_name="sql-function-missing-returns", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="an unsupported template namespace in a function header", case_name="sql-function-template-namespace", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), NativeAttachmentErrorTestCase( description="an unknown variable in a source description", case_name="source-description-unknown-variable", - expected_macro_calls=(1, 1, 1), + expected_macro_calls=(1, 1), expected_error_types=_ERROR_TYPES, ), ], @@ -136,7 +136,7 @@ def test_given_attachment_error_when_compiling_with_each_engine_then_errors_and_ tuple(macro_calls), tuple(error_types), ) == ( - (1, 1, 1), + (1, 1), True, True, True, @@ -145,8 +145,8 @@ def test_given_attachment_error_when_compiling_with_each_engine_then_errors_and_ test_case.expected_error_types, ), ( runs[0].report, - runs[2].report, - runs[2].stderr, + runs[1].report, + runs[1].stderr, ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py index 3b48472302..76762a2cc5 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py @@ -11,5 +11,4 @@ class NativeFastLineageCliTestCase: files: dict[str, str] lineage_targets: tuple[str, ...] expected_lineage: dict[str, dict[str, object]] - expected_minimum_python_fallback_parses: int expected_minimum_traced_edges: int diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py index 1c472b1b45..ae581195b3 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py @@ -1,4 +1,4 @@ -"""The native engines build fast column lineage natively with Python's exact CLI output.""" +"""Both native engines build fast column lineage natively with the expected CLI output.""" from __future__ import annotations @@ -15,7 +15,7 @@ model_lineage_summaries, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _PROJECT_FILES: dict[str, str] = { "sqlbuild_project.toml": ( 'name = "orders_lineage"\nadapter = "duckdb"\n\n[connection]\ndatabase = "orders.duckdb"\n' @@ -86,7 +86,6 @@ "has_star": True, } }, - expected_minimum_python_fallback_parses=2, expected_minimum_traced_edges=4, ), NativeFastLineageCliTestCase( @@ -107,7 +106,6 @@ "has_star": False, }, }, - expected_minimum_python_fallback_parses=2, expected_minimum_traced_edges=2, ), ], @@ -125,21 +123,19 @@ def test_given_project_when_compiling_with_each_engine_then_fast_lineage_output_ ) for engine in _ENGINES } - python: EngineLineageRun = runs["python"] - summaries: dict[str, object] = model_lineage_summaries(python.compile_report) + native: EngineLineageRun = runs["native"] + summaries: dict[str, object] = model_lineage_summaries(native.compile_report) - assert python.compile_returncode == 0, python.compile_report - assert [code for code, _, _ in python.traces] == [0] * len(python.traces) + assert native.compile_returncode == 0, native.compile_report + assert [code for code, _, _ in native.traces] == [0] * len(native.traces) assert {name: summaries[name] for name in test_case.expected_lineage} == ( test_case.expected_lineage ) - assert sum('"source"' in stdout for _, stdout, _ in python.traces) >= ( + assert sum('"source"' in stdout for _, stdout, _ in native.traces) >= ( test_case.expected_minimum_traced_edges ) - assert python.fallback_parses >= test_case.expected_minimum_python_fallback_parses - assert runs["native"]._replace(fallback_parses=python.fallback_parses) == python - assert runs["native-preview"]._replace(fallback_parses=python.fallback_parses) == python - assert (runs["native"].fallback_parses, runs["native-preview"].fallback_parses) == (0, 0) + assert runs["native-preview"] == native + assert native.fallback_parses == 0 if __name__ == "__main__": diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py index 591fd47e67..3cdb726d46 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py @@ -39,6 +39,7 @@ import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.diagnostics.recovery as diagnostic_recovery import sqlbuild.compiler.compile._helpers.macro_bridge.call_store as call_store_module +import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_assembly import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stages import sqlbuild.compiler.compile._helpers.native_stages.sql_tests as native_sql_test_stage import sqlbuild.compiler.contracts.main.promotion_conflicts as promotion_conflicts @@ -76,7 +77,7 @@ from sqlbuild.compiler.frontier.constants import ( COMPILER_CACHE_DIRECTORY_NAME, COMPILER_ENGINE_ENV_VAR, - ENGINE_CACHE_NAMESPACE_SUFFIXES, + RETIRED_CACHE_NAMESPACE_SUFFIXES, ) from sqlbuild.compiler.frontier.main.compiler_cache_directory import compiler_cache_directory from sqlbuild.compiler.macro_bridge.constants import MACRO_CALL_STORE_FILE_NAME @@ -4215,7 +4216,7 @@ def write_retired_compiler_cache_files(project_dir: Path) -> tuple[Path, ...]: / f"{COMPILER_CACHE_DIRECTORY_NAME}{suffix}" / RETIRED_FACT_CACHE_DIRECTORY_NAME / "sql-tests.sqlite3" - for suffix in ENGINE_CACHE_NAMESPACE_SUFFIXES.values() + for suffix in RETIRED_CACHE_NAMESPACE_SUFFIXES ] written.extend( compiler_cache_directory(project_dir) / REUSE_ENTRY_DIRECTORY_NAME / name @@ -4295,9 +4296,9 @@ def ignore_project_changes(monkeypatch: pytest.MonkeyPatch) -> None: def ignore_query_in_analysis_key(monkeypatch: pytest.MonkeyPatch) -> None: """Key Python's cached model analyses without their query, so an edited query reads a stale - analysis; the Python engine runs, since native analysis keys its own cache.""" + analysis; native analysis defers to that Python analysis, since it keys its own cache.""" - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, "python") + monkeypatch.setattr(native_assembly, "analyze_native_model_sql", lambda **_: None) analysis_key: Callable[..., str] = project_assembly.model_analysis_cache_key def query_blind_key(**kwargs: Any) -> str: @@ -4406,23 +4407,6 @@ def engine_reuse_compile(*, project_dir: Path, engine: str) -> CompileReuseRun: return run_reuse_compile(project_dir=project_dir, global_args=("--compiler-engine", engine)) -def environment_engine_reuse_compile(*, project_dir: Path, engine: str) -> CompileReuseRun: - """Compile with reuse enabled under the engine the environment selects; empty means default.""" - - return run_reuse_compile(project_dir=project_dir, env={COMPILER_ENGINE_ENV_VAR: engine}) - - -def engine_compile_and_rules(*, project_dir: Path, engine: str, rules_selector: str) -> int: - """Compile and run Rules under one engine; return the Rules exit code.""" - - _ = engine_reuse_compile(project_dir=project_dir, engine=engine) - return run_installed_sqb( - project_dir=project_dir, - args=("--compiler-engine", engine, "rules", "run", rules_selector), - env=COMPILE_REUSE_ENV, - ).returncode - - def report_engine(run: CompileReuseRun) -> str: """Return the engine named in a JSON compile report.""" @@ -5087,20 +5071,6 @@ def fallback(**_kwargs: object) -> object: return run, called -def python_engine_compile( - *, project_dir: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] -) -> CompileReuseRun: - """Compile under the Python engine in this process.""" - - with monkeypatch.context() as patch: - for variable, value in { - COMPILER_ENGINE_ENV_VAR: "python", - REUSE_DISABLE_ENV_VAR: "1", - }.items(): - patch.setenv(variable, value) - return in_process_reuse_run(project_dir=project_dir, capsys=capsys) - - def write_counted_error_project(*, project_dir: Path, files: dict[str, str]) -> None: """Write the failure base project with `files` added or replaced into a fresh directory.""" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py index f0559ee404..80ccee60ec 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py @@ -1,4 +1,4 @@ -"""The native engines complete semantic diagnostics natively with Python's exact CLI output.""" +"""Both native engines complete semantic diagnostics natively with the expected CLI output.""" from __future__ import annotations @@ -16,7 +16,7 @@ engine_semantic_run, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CONFIG: str = ( 'name = "orders_semantics"\nadapter = "duckdb"\n\n[connection]\ndatabase = "orders.duckdb"\n' ) @@ -124,16 +124,13 @@ def test_given_failing_project_when_compiling_with_each_engine_then_diagnostics_ ) for engine in _ENGINES } - python: EngineSemanticRun = runs["python"] native: EngineSemanticRun = runs["native"] preview: EngineSemanticRun = runs["native-preview"] - assert python.returncode == 1 - assert diagnostic_codes(python.report) == test_case.expected_codes - assert set(test_case.expected_notes) <= diagnostic_notes(python.report) - assert python.semantic_wheel_calls > 0 - assert (native.report, native.returncode) == (python.report, python.returncode) - assert (preview.report, preview.returncode) == (python.report, python.returncode) + assert native.returncode == 1 + assert diagnostic_codes(native.report) == test_case.expected_codes + assert set(test_case.expected_notes) <= diagnostic_notes(native.report) + assert (preview.report, preview.returncode) == (native.report, native.returncode) assert (native.semantic_wheel_calls, preview.semantic_wheel_calls) == (0, 0) assert native.deferrals == preview.deferrals == test_case.expected_native_deferrals diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/sql_test_errors/test_native_sql_test_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/sql_test_errors/test_native_sql_test_errors.py index 411ed75228..1f16be8069 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/sql_test_errors/test_native_sql_test_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/sql_test_errors/test_native_sql_test_errors.py @@ -25,7 +25,7 @@ NativeSqlTestErrorTestCase, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _COUNTED_MACRO: str = ( "from pathlib import Path\n\n\n" @@ -154,7 +154,7 @@ def test_given_sql_test_error_when_compiling_with_each_engine_then_errors_and_ty len(set(macro_calls)), tuple(error_types), ) == ( - (1, 1, 1), + (1, 1), True, True, True, @@ -162,11 +162,11 @@ def test_given_sql_test_error_when_compiling_with_each_engine_then_errors_and_ty True, True, test_case.expected_distinct_macro_call_counts, - ("CompileInputError", "CompileInputError", "CompileInputError"), + ("CompileInputError", "CompileInputError"), ), ( runs[0].report, runs[0].stderr, - runs[2].report, + runs[1].report, macro_calls, ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_macro_call_store.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_macro_call_store.py index a8734227cc..daeb1db80a 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_macro_call_store.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_macro_call_store.py @@ -289,12 +289,6 @@ def test_given_broken_store_key_when_compiling_an_edit_then_the_oracle_reports_a expected_logged_calls=(1, 0), expected_store_files=("target/cache/compiler-native-v1/macro-calls.bin",), ), - EngineMacroCallGateTestCase( - description="python", - engine="python", - expected_logged_calls=(3, 3), - expected_store_files=(), - ), EngineMacroCallGateTestCase( description="native", engine="native", @@ -310,7 +304,7 @@ def test_given_broken_store_key_when_compiling_an_edit_then_the_oracle_reports_a ], ids=lambda case: case.description, ) -def test_given_engine_when_compiling_repeatedly_then_only_native_engines_batch_and_store_macro_calls( +def test_given_engine_when_compiling_repeatedly_then_macro_calls_are_batched_and_stored( tmp_path: Path, test_case: EngineMacroCallGateTestCase ) -> None: project_dir: Path = tmp_path / "project" @@ -333,11 +327,6 @@ def test_given_engine_when_compiling_repeatedly_then_only_native_engines_batch_a @pytest.mark.parametrize( "test_case", [ - MacroReferenceCallStoreTestCase( - description="python", - engine="python", - expected_logged_calls=(2, 2), - ), MacroReferenceCallStoreTestCase( description="native_memo_then_store", engine="native", @@ -346,13 +335,13 @@ def test_given_engine_when_compiling_repeatedly_then_only_native_engines_batch_a ], ids=lambda case: case.description, ) -def test_given_macro_returning_rejected_reference_call_when_replaying_then_reports_match_python( +def test_given_macro_returning_rejected_reference_call_when_replaying_then_reports_match_first( tmp_path: Path, test_case: MacroReferenceCallStoreTestCase ) -> None: oracle: MacroReferenceCallRuns = macro_reference_call_runs( - project_dir=prepare_macro_reference_call_project(tmp_path=tmp_path / "python"), - log_path=tmp_path / "python.log", - engine="python", + project_dir=prepare_macro_reference_call_project(tmp_path=tmp_path / "oracle"), + log_path=tmp_path / "oracle.log", + engine="native", runs=1, ) @@ -518,7 +507,7 @@ def test_given_no_proc_filesystem_when_compiling_repeatedly_then_the_store_still description="a lone surrogate var is rejected before any macro runs", project_files=_TAGGED_STAGING, compile_args=("--vars", '{"regions": ["\\udcff"]}'), - expected_returncodes=(1, 1, 1), + expected_returncodes=(1, 1), expected_report_fragment=( "Variable 'regions' holds a lone surrogate in its value[0] at UTF-8 byte 0, " "which is not valid Unicode text" @@ -532,7 +521,7 @@ def test_given_unencodable_var_when_compiling_then_every_engine_rejects_it( ) -> None: project_dir: Path = tmp_path / "orders" runs: list[CompileReuseRun] = [] - for engine in ("python", "native", "native-preview"): + for engine in ("native", "native-preview"): shutil.rmtree(project_dir, ignore_errors=True) for relative_path, contents in {**FAILURE_BASE_FILES, **test_case.project_files}.items(): write_project_file(project_dir, relative_path, contents) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_switch.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_switch.py deleted file mode 100644 index f8b5f2195f..0000000000 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_switch.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Both compiler engines produce identical compiles and never share on-disk compile stores.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from tests.e2e.src.sqlbuild.cli.commands.main.compile._test_types import ( - CompilerEngineMacroParityTestCase, - CompilerEngineParityTestCase, - CompilerEngineRulesStoreTestCase, - CompilerEngineStoreTestCase, - DefaultEngineParityTestCase, -) -from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import ( - CompileReuseRun, - copy_compile_project, - engine_compile_and_rules, - engine_reuse_compile, - environment_engine_reuse_compile, - prepare_compile_reuse_project, - report_engine, - report_without_engine, - store_digests, - store_files, -) -from tests.integration.src.sqlbuild.compiler.compile.helpers import ( - MACRO_BRIDGE_PROJECT_FILES, - write_project, -) - - -@pytest.mark.parametrize( - "test_case", - [ - CompilerEngineParityTestCase( - description="python_and_native", - left_engine="python", - right_engine="native", - expected_engines=("python", "native"), - expected_exit_codes=(0, 0), - ) - ], - ids=lambda case: case.description, -) -def test_given_same_project_when_compiling_with_each_engine_then_outputs_are_identical( - test_case: CompilerEngineParityTestCase, tmp_path: Path -) -> None: - prepared_project: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=prepared_project) - left: CompileReuseRun = engine_reuse_compile( - project_dir=copy_compile_project(source=prepared_project, destination=tmp_path / "left"), - engine=test_case.left_engine, - ) - right: CompileReuseRun = engine_reuse_compile( - project_dir=copy_compile_project(source=prepared_project, destination=tmp_path / "right"), - engine=test_case.right_engine, - ) - - assert (left.returncode, right.returncode) == test_case.expected_exit_codes - assert (report_engine(left), report_engine(right)) == test_case.expected_engines - assert report_without_engine(right) == report_without_engine(left) - assert right.compiled == left.compiled - assert left.compiled - - -@pytest.mark.parametrize( - "test_case", - [ - DefaultEngineParityTestCase( - description="python_oracle_and_unset_default", - oracle_engine="python", - default_engine="", - expected_engines=("python", "native"), - expected_exit_codes=(0, 0), - expected_macro_call_stores=((), ("target/cache/compiler-native-v1/macro-calls.bin",)), - ) - ], - ids=lambda case: case.description, -) -def test_given_no_engine_selection_when_compiling_then_native_rendering_runs_and_matches_python( - test_case: DefaultEngineParityTestCase, tmp_path: Path -) -> None: - prepared_project: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=prepared_project) - oracle_dir: Path = copy_compile_project( - source=prepared_project, destination=tmp_path / "oracle" - ) - default_dir: Path = copy_compile_project( - source=prepared_project, destination=tmp_path / "default" - ) - oracle: CompileReuseRun = environment_engine_reuse_compile( - project_dir=oracle_dir, engine=test_case.oracle_engine - ) - default: CompileReuseRun = environment_engine_reuse_compile( - project_dir=default_dir, engine=test_case.default_engine - ) - - assert (oracle.returncode, default.returncode) == test_case.expected_exit_codes - assert (report_engine(oracle), report_engine(default)) == test_case.expected_engines - assert report_without_engine(default) == report_without_engine(oracle) - assert default.compiled == oracle.compiled - assert oracle.compiled - assert ( - tuple(store_files(oracle_dir)), - tuple(store_files(default_dir)), - ) == test_case.expected_macro_call_stores - - -@pytest.mark.parametrize( - "test_case", - [ - CompilerEngineMacroParityTestCase( - description="macros_in_models_hooks_tests_audits_sources_functions", - files={ - path: content.replace("WHERE id IN @generated_join()\n", "") - for path, content in MACRO_BRIDGE_PROJECT_FILES.items() - }, - expected_exit_codes=(0, 0), - expected_compiled_fragments=("amount * 21", "'__SQLBUILD_RELATION_1__'"), - ) - ], - ids=lambda case: case.description, -) -def test_given_macro_heavy_project_when_compiling_with_each_engine_then_outputs_are_identical( - test_case: CompilerEngineMacroParityTestCase, tmp_path: Path -) -> None: - python_run: CompileReuseRun = engine_reuse_compile( - project_dir=write_project(root=tmp_path / "python", files=test_case.files), - engine="python", - ) - native_run: CompileReuseRun = engine_reuse_compile( - project_dir=write_project(root=tmp_path / "native", files=test_case.files), - engine="native", - ) - compiled_text: str = b"".join(python_run.compiled.values()).decode() - - assert (python_run.returncode, native_run.returncode) == test_case.expected_exit_codes - assert report_without_engine(native_run) == report_without_engine(python_run) - assert native_run.compiled == python_run.compiled - assert all(map(compiled_text.__contains__, test_case.expected_compiled_fragments)) - - -@pytest.mark.parametrize( - "test_case", - [ - CompilerEngineStoreTestCase( - description="python_native_native_python", - engines=("python", "native", "native", "python"), - expected_reused=(False, False, True, True), - expected_python_stores=("target/cache/compiler", "target/rules-cache"), - expected_native_stores=( - "target/cache/compiler-native-v1", - "target/rules-cache-native-v1", - ), - ) - ], - ids=lambda case: case.description, -) -def test_given_compile_by_one_engine_when_other_engine_compiles_then_no_store_is_shared( - test_case: CompilerEngineStoreTestCase, tmp_path: Path -) -> None: - project_dir: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=project_dir) - - first: CompileReuseRun = engine_reuse_compile( - project_dir=project_dir, engine=test_case.engines[0] - ) - python_stores: dict[str, str] = store_digests( - project_dir=project_dir, stores=test_case.expected_python_stores - ) - later: list[CompileReuseRun] = [ - engine_reuse_compile(project_dir=project_dir, engine=engine) - for engine in test_case.engines[1:-1] - ] - python_stores_after_native: dict[str, str] = store_digests( - project_dir=project_dir, stores=test_case.expected_python_stores - ) - last: CompileReuseRun = engine_reuse_compile( - project_dir=project_dir, engine=test_case.engines[-1] - ) - runs: list[CompileReuseRun] = [first, *later, last] - - assert tuple(run.reused for run in runs) == test_case.expected_reused - assert python_stores - assert python_stores_after_native == python_stores - assert store_digests(project_dir=project_dir, stores=test_case.expected_native_stores) - assert {report_without_engine(run) for run in runs[2:]} == {report_without_engine(last)} - - -@pytest.mark.parametrize( - "test_case", - [ - CompilerEngineRulesStoreTestCase( - description="built_in_rules_after_compile", - rules_selector="SQBR", - stores=("target/cache", "target/rules-cache", "target/rules-cache-native-v1"), - native_marker="-native-v1/", - expected_python_files=( - "target/rules-cache/bulk/native.json", - "target/rules-cache/bulk/sql.json", - ), - expected_native_files=( - "target/rules-cache-native-v1/bulk/native.json", - "target/rules-cache-native-v1/bulk/sql.json", - ), - ) - ], - ids=lambda case: case.description, -) -def test_given_python_compile_and_rules_when_native_runs_them_then_native_writes_only_its_stores( - test_case: CompilerEngineRulesStoreTestCase, tmp_path: Path -) -> None: - project_dir: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=project_dir) - - python_exit: int = engine_compile_and_rules( - project_dir=project_dir, engine="python", rules_selector=test_case.rules_selector - ) - python_files: dict[str, str] = store_digests(project_dir=project_dir, stores=test_case.stores) - native_exit: int = engine_compile_and_rules( - project_dir=project_dir, engine="native", rules_selector=test_case.rules_selector - ) - all_files: dict[str, str] = store_digests(project_dir=project_dir, stores=test_case.stores) - native_files: set[str] = set(all_files) - set(python_files) - - assert native_exit == python_exit - assert {path: all_files.get(path) for path in python_files} == python_files - assert all(test_case.native_marker in path for path in native_files), sorted(native_files) - assert set(test_case.expected_python_files) <= set(python_files) - assert set(test_case.expected_native_files) <= native_files - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_tiers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_tiers.py index 519651f2d4..812b5c4841 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_tiers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compiler_engine_tiers.py @@ -1,4 +1,4 @@ -"""The native-preview tier matches Python and never shares stores with the shipped native tier.""" +"""The native-preview tier matches shipped native and never shares stores with it.""" from __future__ import annotations @@ -25,16 +25,16 @@ "test_case", [ CompilerEngineParityTestCase( - description="python_and_native_preview", - left_engine="python", + description="native_and_native_preview", + left_engine="native", right_engine="native-preview", - expected_engines=("python", "native-preview"), + expected_engines=("native", "native-preview"), expected_exit_codes=(0, 0), ) ], ids=lambda case: case.description, ) -def test_given_same_project_when_compiling_with_preview_tier_then_outputs_match_python( +def test_given_same_project_when_compiling_with_preview_tier_then_outputs_match_native( test_case: CompilerEngineParityTestCase, tmp_path: Path ) -> None: prepared_project: Path = tmp_path / "orders" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_deep_header_nesting.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_deep_header_nesting.py index 62a291291b..0a782253b9 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_deep_header_nesting.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_deep_header_nesting.py @@ -18,7 +18,7 @@ _ORDERS_MODEL: str = "MODEL (description 'Orders.');\n\nSELECT 1 AS order_id\n" _COMPILE_TIME_BOUND_SECONDS: float = 120.0 _NESTING_HELP: str = "help: flatten the value so it nests at most 256 levels deep" -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CONSTANT_CALL_NESTING: tuple[str, ...] = ( "error[D013]", "constants/limits.sql:3' contains invalid SQLBuild header syntax: " @@ -31,8 +31,8 @@ "test_case", [ DeepHeaderNestingCompileCase( - description="python_model_1k", - engine="python", + description="native_model_1k", + engine="native", path="models/orders.sql", prefix="MODEL (\n description 'Orders.',\n tags ", suffix=",\n);\n\nSELECT 1 AS order_id\n", @@ -72,20 +72,6 @@ _NESTING_HELP, ), ), - DeepHeaderNestingCompileCase( - description="python_hook_100k", - engine="python", - path="hooks/sql/record_refresh.sql", - prefix="HOOK (\n description ", - suffix=",\n);\n\nSELECT 1\n", - depth=100_000, - expected_fragments=( - "error[D014]", - "hooks/sql/record_refresh.sql:2' contains invalid SQLBuild header syntax: " - "values nest deeper than 256 levels", - _NESTING_HELP, - ), - ), ], ids=lambda case: case.description, ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dollar_quoted_project_vars.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dollar_quoted_project_vars.py index 3919c76bc8..6e25b09ae7 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dollar_quoted_project_vars.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dollar_quoted_project_vars.py @@ -12,7 +12,7 @@ ) from tests.e2e.src.sqlbuild.cli.commands.shared.helpers import prepare_inline_project, run_sqb -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _PROJECT_TOML: str = 'name = "orders"\nadapter = "duckdb"\n\n[vars]\nregion = "north"\n' _MODEL_HEADER: str = "MODEL (description 'Order region labels.', materialized view);\n\n" _COMPILED_MODEL: Path = Path("target/compiled/models/order_labels.sql") diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py index a2d9676e5c..56dd2c7a3c 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py @@ -26,7 +26,7 @@ report_without_engine, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") @pytest.mark.parametrize( @@ -70,14 +70,12 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ ) for engine in _ENGINES } - python_run, python_seams = outcomes["python"] native_run, native_seams = outcomes["native"] preview_run, preview_seams = outcomes["native-preview"] - assert python_run.returncode == 0, python_run.stderr - assert python_run.compiled - assert (python_seams, native_seams, preview_seams) == ( - {}, + assert native_run.returncode == 0, native_run.stderr + assert native_run.compiled + assert (native_seams, preview_seams) == ( test_case.expected_native_returns, test_case.expected_native_returns, ) @@ -102,11 +100,9 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ assert [ len(call.args[0].tests) for call in native_sql_test_planning.call_args_list ] == planned_counts - assert (native_run.returncode, preview_run.returncode) == (0, 0) - assert report_without_engine(native_run) == report_without_engine(python_run) - assert report_without_engine(preview_run) == report_without_engine(python_run) - assert native_run.compiled == python_run.compiled - assert preview_run.compiled == python_run.compiled + assert preview_run.returncode == 0 + assert report_without_engine(preview_run) == report_without_engine(native_run) + assert preview_run.compiled == native_run.compiled if __name__ == "__main__": diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_config_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_config_errors.py index 2bfc85664d..430bb6b4eb 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_config_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_config_errors.py @@ -1,4 +1,4 @@ -"""The preview engine reports model config, template and header errors as Python does.""" +"""Both engines report model config, template and header errors alike, without fallbacks.""" from __future__ import annotations @@ -16,14 +16,13 @@ from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import ( CompileReuseRun, fallback_free_preview_compile, - python_engine_compile, report_without_engine, run_reuse_compile, stderr_without_durations, write_counted_error_project, ) -_ENGINES: tuple[str, str] = ("python", "native-preview") +_ENGINES: tuple[str, str] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _COUNTED_MACRO: str = ( "from pathlib import Path\n\n\n" @@ -119,7 +118,7 @@ ], ids=lambda case: case.description, ) -def test_given_config_error_when_compiling_with_preview_then_error_matches_python( +def test_given_config_error_when_compiling_with_each_engine_then_errors_match( test_case: NativeConfigErrorTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -136,12 +135,8 @@ def test_given_config_error_when_compiling_with_preview_then_error_matches_pytho run_reuse_compile(project_dir=project_dir, global_args=("--compiler-engine", engine)) ) macro_calls.append(len((project_dir / _CALL_LOG).read_text(encoding="utf-8").splitlines())) - for name in ("python", "preview"): - write_counted_error_project( - project_dir=tmp_path / name, files={**_PROJECT_FILES, **test_case.project_files} - ) - python_run: CompileReuseRun = python_engine_compile( - project_dir=tmp_path / "python", monkeypatch=monkeypatch, capsys=capsys + write_counted_error_project( + project_dir=tmp_path / "preview", files={**_PROJECT_FILES, **test_case.project_files} ) preview_run, fallbacks = fallback_free_preview_compile( project_dir=tmp_path / "preview", @@ -158,7 +153,7 @@ def test_given_config_error_when_compiling_with_preview_then_error_matches_pytho test_case.expected_report_text in runs[0].report + runs[0].stderr, macro_calls, preview_run.returncode, - report_without_engine(preview_run).replace("preview", "python"), + report_without_engine(preview_run).replace("preview", "orders"), fallbacks, ) == ( (1, 1), @@ -166,8 +161,8 @@ def test_given_config_error_when_compiling_with_preview_then_error_matches_pytho True, True, test_case.expected_macro_calls, - python_run.returncode, - report_without_engine(python_run).replace("preview", "python"), + 1, + report_without_engine(runs[1]), test_case.expected_python_fallbacks, ), (runs[0].report, runs[1].report, runs[1].stderr) @@ -185,9 +180,9 @@ def test_given_config_error_when_compiling_with_preview_then_error_matches_pytho ), _CALL_LOG: "", }, - engines=("python", "native-preview", "native"), + engines=("native-preview", "native"), expected_report_text="references unknown model 'ghost'", - expected_macro_calls=[1, 1, 1], + expected_macro_calls=[1, 1], ) ], ids=lambda case: case.description, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_declaration_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_declaration_errors.py index 2edee79840..764a12b7eb 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_declaration_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_declaration_errors.py @@ -21,7 +21,7 @@ write_project_file, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _COUNTED_MACRO: str = ( "from pathlib import Path\n\n\n" @@ -156,7 +156,7 @@ def test_given_declaration_error_when_compiling_natively_then_error_matches_pyth **_STATUS_ENUM, _STAGING_PATH: _STAGING_PREFIX + 'status = @enum("missing_status").PLACED\n', }, - expected_error_types=("CompileInputError",) * 3, + expected_error_types=("CompileInputError",) * 2, ), DeclarationErrorLifecycleTestCase( description="unclosed_quote_after_a_reference_only_python_scans", @@ -164,7 +164,7 @@ def test_given_declaration_error_when_compiling_natively_then_error_matches_pyth **_STATUS_ENUM, _STAGING_PATH: _STAGING_PREFIX + "status = @enum\u00e9 OR status = 'open\n", }, - expected_error_types=("CompileInputError",) * 3, + expected_error_types=("CompileInputError",) * 2, ), ], ids=lambda case: case.description, @@ -173,7 +173,7 @@ def test_given_declaration_error_when_compiling_with_each_engine_then_lifecycle_ test_case: DeclarationErrorLifecycleTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: error_types: list[str] = [] - for engine in ("python", "native", "native-preview"): + for engine in ("native", "native-preview"): project_dir: Path = tmp_path / engine for relative_path, contents in { **FAILURE_BASE_FILES, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_macro_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_macro_errors.py index eec6cd6f8e..247cb40057 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_macro_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_macro_errors.py @@ -21,7 +21,7 @@ write_project_file, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _MACROS: str = ( "from pathlib import Path\n\n\n" @@ -180,12 +180,12 @@ def test_given_macro_error_when_compiling_with_each_engine_then_error_matches_py MacroErrorLifecycleTestCase( description="macro_raises_after_a_side_effect", project_files={_STAGING_PATH: _STAGING_PREFIX + "@explode('amount')" + _STAGING_SUFFIX}, - expected_error_types=("CompileInputError",) * 3, + expected_error_types=("CompileInputError",) * 2, ), MacroErrorLifecycleTestCase( description="unknown_macro", project_files={_STAGING_PATH: _STAGING_PREFIX + "@missing('amount')" + _STAGING_SUFFIX}, - expected_error_types=("CompileInputError",) * 3, + expected_error_types=("CompileInputError",) * 2, ), ], ids=lambda case: case.description, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_config.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_config.py deleted file mode 100644 index ba53f0d8dd..0000000000 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_config.py +++ /dev/null @@ -1,129 +0,0 @@ -"""Native model config in the default engine compiles exactly as the Python engine does.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from tests.e2e.src.sqlbuild.cli.commands.main.compile._test_types import ( - NativeModelConfigParityTestCase, -) -from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import ( - CompileReuseRun, - copy_compile_project, - prepare_compile_reuse_project, - replace_project_text, - report_without_engine, - run_reuse_compile, - stderr_without_durations, - write_project_file, -) - -_ORDER_QUALITY_MODEL: str = """MODEL ( - materialized table, - description "Order quality checks.", - schema "${coalesce(ENV:SQB_ORDER_QUALITY_SCHEMA, 'quality')}_${CTX:run.target}", - tags ["${if(eq(CTX:run.target, 'dev'), 'development', 'release')}"], - audits [ - expression_is_true ( - name "quantity_is_positive", - expression "quantity > 0", - severity error, - ), - ], - columns ( - order_id ( - type INTEGER, - nullable false, - description "Order key", - audits [not_null (severity error, name order_id_present), unique], - ), - status ( - type VARCHAR, - audits [ - accepted_values ( - values ["placed", "preparing", "ready", "completed", "cancelled"], - severity warn, - description "Known statuses", - always_run true, - ), - ], - ), - Quantity (type INTEGER, description "Waffles ordered"), - ), -); - -SELECT order_id, status, quantity AS Quantity -FROM __ref("stg_orders") -""" -_INVALID_SEVERITY_MODEL: str = """MODEL ( - materialized table, - columns ( - order_id (audits [not_null (severity fatal)]), - ), -); - -SELECT order_id FROM __ref("stg_orders") -""" -_TARGET_SCHEMA: tuple[str, str] = ( - 'schema = "dev"', - "schema = \"${coalesce(ENV:SQB_TARGET_SCHEMA, ENV:SQB_MISSING_SCHEMA, 'dev')}\"", -) - - -@pytest.mark.parametrize( - "test_case", - [ - NativeModelConfigParityTestCase( - description="header_metadata_and_templates", - project_files={"models/marts/order_quality.sql": _ORDER_QUALITY_MODEL}, - project_config_replacements=(_TARGET_SCHEMA,), - environment={"SQB_TARGET_SCHEMA": "analytics"}, - engines=("python", "native"), - expected_exit_codes=(0, 0), - expected_report_text="order_quality", - ), - NativeModelConfigParityTestCase( - description="python_error_for_an_invalid_audit_severity", - project_files={"models/marts/order_quality.sql": _INVALID_SEVERITY_MODEL}, - project_config_replacements=(), - environment={}, - engines=("python", "native"), - expected_exit_codes=(1, 1), - expected_report_text="severity", - ), - ], - ids=lambda case: case.description, -) -def test_given_authored_model_config_when_compiling_natively_then_output_matches_python( - test_case: NativeModelConfigParityTestCase, tmp_path: Path -) -> None: - prepared_project: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=prepared_project) - for relative_path, contents in test_case.project_files.items(): - write_project_file(prepared_project, relative_path, contents) - for old, new in test_case.project_config_replacements: - replace_project_text(prepared_project, "sqlbuild_project.toml", old, new) - runs: list[CompileReuseRun] = [ - run_reuse_compile( - project_dir=copy_compile_project( - source=prepared_project, destination=tmp_path / engine - ), - env=test_case.environment, - global_args=("--compiler-engine", engine), - ) - for engine in test_case.engines - ] - - assert tuple(run.returncode for run in runs) == test_case.expected_exit_codes - assert report_without_engine(runs[1]) == report_without_engine(runs[0]) - assert stderr_without_durations(stderr=runs[1].stderr) == stderr_without_durations( - stderr=runs[0].stderr - ) - assert runs[1].compiled == runs[0].compiled - assert test_case.expected_report_text in runs[0].report + runs[0].stderr - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_loop.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_loop.py deleted file mode 100644 index 3cbf097d98..0000000000 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_model_loop.py +++ /dev/null @@ -1,106 +0,0 @@ -"""The default engine's native model loop compiles exactly as the Python engine does.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from tests.e2e.src.sqlbuild.cli.commands.main.compile._test_types import ( - NativeModelLoopParityTestCase, -) -from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import ( - CompileReuseRun, - copy_compile_project, - prepare_compile_reuse_project, - report_without_engine, - run_reuse_compile, - stderr_without_durations, - write_project_file, -) - -_DECLARATIONS: dict[str, str] = { - "enums/order_status.sql": "ENUM (name order_status, members [PLACED, SHIPPED]);\n", - "constants/limits.sql": "CONSTANT (name order_cap, value 3);\n", -} -_MARTS_DECLARATIONS: dict[str, str] = { - "models/marts/_sqlbuild/_enums/order_status.sql": ( - "ENUM (name order_status, members [PLACED, SHIPPED]);\n" - ), - "models/marts/_sqlbuild/_constants/limits.sql": "CONSTANT (name order_cap, value 3);\n", -} -_DECLARATION_MODEL: str = """MODEL (materialized view, description "Order flags."); - -SELECT - order_id, - @enum("order_status").PLACED AS placed_status, - @const("order_cap") AS order_cap, - '@const("order_cap")' AS literal_text -FROM __ref("stg_orders") -""" - - -@pytest.mark.parametrize( - "test_case", - [ - NativeModelLoopParityTestCase( - description="folder_private_declaration_references", - project_files={ - **_MARTS_DECLARATIONS, - "models/marts/order_flags.sql": _DECLARATION_MODEL, - }, - engines=("python", "native"), - expected_exit_codes=(0, 0), - expected_report_text="order_flags", - ), - NativeModelLoopParityTestCase( - description="python_identity_error_for_a_folder_containing_a_colon", - project_files={**_DECLARATIONS, "models/a:b/order_flags.sql": _DECLARATION_MODEL}, - engines=("python", "native"), - expected_exit_codes=(1, 1), - expected_report_text="Unknown qualified identity kind", - ), - NativeModelLoopParityTestCase( - description="rejected_reference_call_beside_declaration_references", - project_files={ - **_MARTS_DECLARATIONS, - "models/marts/order_flags.sql": _DECLARATION_MODEL.replace( - '__ref("stg_orders")', "__ref('stg_orders')" - ), - }, - engines=("python", "native"), - expected_exit_codes=(1, 1), - expected_report_text="P012", - ), - ], - ids=lambda case: case.description, -) -def test_given_model_declarations_when_compiling_natively_then_output_matches_python( - test_case: NativeModelLoopParityTestCase, tmp_path: Path -) -> None: - prepared_project: Path = tmp_path / "orders" - prepare_compile_reuse_project(project_dir=prepared_project) - for relative_path, contents in test_case.project_files.items(): - write_project_file(prepared_project, relative_path, contents) - runs: list[CompileReuseRun] = [ - run_reuse_compile( - project_dir=copy_compile_project( - source=prepared_project, destination=tmp_path / engine - ), - env={}, - global_args=("--compiler-engine", engine), - ) - for engine in test_case.engines - ] - - assert tuple(run.returncode for run in runs) == test_case.expected_exit_codes - assert report_without_engine(runs[1]) == report_without_engine(runs[0]) - assert stderr_without_durations(stderr=runs[1].stderr) == stderr_without_durations( - stderr=runs[0].stderr - ) - assert runs[1].compiled == runs[0].compiled - assert test_case.expected_report_text in runs[0].report + runs[0].stderr - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_reference_errors.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_reference_errors.py index 97e099d6ec..9904820753 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_reference_errors.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_reference_errors.py @@ -24,7 +24,7 @@ write_project_file, ) -_ENGINES: tuple[str, str, str] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _CALL_LOG: str = "macro_calls.log" _EMIT_MACRO: str = ( "from pathlib import Path\n\n\n" @@ -53,8 +53,8 @@ expected_message=( f"{FAILURE_MART_PATH}:6:6: SQL reference contains an unclosed parenthesis" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="unclosed_quote_after_a_rejected_call", @@ -66,8 +66,8 @@ expected_message=( f"{FAILURE_MART_PATH}:7:14: SQL reference contains an unclosed quoted string" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="empty_table_function_argument", @@ -78,8 +78,8 @@ expected_message=( f"{FAILURE_MART_PATH}:6:31: SQL reference contains an empty argument" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="header_string_with_an_escaped_quote_and_apostrophe", @@ -90,8 +90,8 @@ expected_message=( f"{FAILURE_MART_PATH}:7:7: SQL reference contains an unclosed quoted string" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="macro_output_fault_before_an_authored_fault_is_unlocated", @@ -104,8 +104,8 @@ expected_message=( f"{FAILURE_MART_PATH}: SQL reference contains an unclosed parenthesis" ), - expected_macro_calls=(1, 1, 1), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(1, 1), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="authored_fault_after_a_macro_is_located_natively", @@ -117,8 +117,8 @@ expected_message=( f"{FAILURE_MART_PATH}:6:6: SQL reference contains an unclosed parenthesis" ), - expected_macro_calls=(1, 1, 1), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(1, 1), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="test_file_header_names_the_file", @@ -137,8 +137,8 @@ expected_message=( "tests/unit/test_customer_totals.sql: SQL reference contains an empty argument" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="audit_file_header_names_the_file", @@ -155,8 +155,8 @@ expected_message=( "audits/generic/positive_total.sql: SQL reference contains an unclosed quoted string" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ReferenceScanErrorTestCase( description="function_file_header_names_the_file", @@ -171,8 +171,8 @@ expected_message=( "functions/sql/orders_for.sql: SQL reference contains an unclosed parenthesis" ), - expected_macro_calls=(0, 0, 0), - expected_error_types=("CompileInputError",) * 3, + expected_macro_calls=(0, 0), + expected_error_types=("CompileInputError",) * 2, ), ], ids=lambda case: case.description, @@ -209,13 +209,13 @@ def test_given_reference_scan_error_when_compiling_with_each_engine_then_located tuple(macro_calls), tuple(error_types), ) == ( - (1, 1, 1), + (1, 1), True, True, True, test_case.expected_macro_calls, test_case.expected_error_types, - ), (runs[0].report, runs[2].report) + ), (runs[0].report, runs[1].report) if __name__ == "__main__": diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py index 8ef07ce0b8..f1600fafb9 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py @@ -1,4 +1,4 @@ -"""Every engine reports contract types and failures alike; native engines compare types natively.""" +"""Both engines report contract types and failures alike and compare types natively.""" from __future__ import annotations @@ -45,9 +45,6 @@ SELECT order_id, amount AS total FROM __ref("orders") """ -_NO_NATIVE_ANSWERS: NativeTypeAnswers = NativeTypeAnswers( - normalized=[], typed_comparisons=0, handbacks=0 -) _IMMEDIATE_PROMOTION_TOML: str = ( _PROJECT_TOML + '\n[settings]\ntable_promotion_mode = "immediate"\n' ) @@ -148,7 +145,7 @@ ], ids=lambda case: case.description, ) -def test_given_contract_types_when_compiling_with_each_engine_then_reports_match_python( +def test_given_contract_types_when_compiling_with_each_engine_then_reports_are_expected( test_case: NativeTypeSystemTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -163,20 +160,15 @@ def test_given_contract_types_when_compiling_with_each_engine_then_reports_match monkeypatch=monkeypatch, capsys=capsys, ) - for engine in ("python", "native", "native-preview") + for engine in ("native", "native-preview") } - python_run, python_answers = outcomes["python"] native_run, native_answers = outcomes["native"] preview_run, preview_answers = outcomes["native-preview"] - assert (python_run.returncode, native_run.returncode, preview_run.returncode) == ( - (test_case.expected_exit_code,) * 3 - ) - assert test_case.expected_report_text in python_run.report - assert report_without_engine(native_run) == report_without_engine(python_run) - assert report_without_engine(preview_run) == report_without_engine(python_run) - assert (native_run.compiled, preview_run.compiled) == (python_run.compiled,) * 2 - assert python_answers == _NO_NATIVE_ANSWERS + assert (native_run.returncode, preview_run.returncode) == (test_case.expected_exit_code,) * 2 + assert test_case.expected_report_text in native_run.report + assert report_without_engine(preview_run) == report_without_engine(native_run) + assert preview_run.compiled == native_run.compiled assert native_answers == preview_answers assert (native_answers.typed_comparisons >= 1, native_answers.handbacks) == (True, 0) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_reference_call_syntax.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_reference_call_syntax.py index d61d49961a..9e35a28c5d 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_reference_call_syntax.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_reference_call_syntax.py @@ -26,11 +26,6 @@ @pytest.mark.parametrize( "test_case", [ - ReplaceableReferenceCallTestCase( - description="python engine builds double quoted references", - engine="python", - expected_total_cents="2850", - ), ReplaceableReferenceCallTestCase( description="native engine builds double quoted references", engine="native", @@ -62,78 +57,6 @@ def test_given_double_quoted_reference_calls_when_building_then_relations_resolv @pytest.mark.parametrize( "test_case", [ - RejectedReferenceCallTestCase( - description="python engine rejects unquoted ref name", - engine="python", - staging_from=_CANONICAL_SOURCE, - mart_from="__ref(stg_orders)", - expected_diagnostics=( - ( - "P012", - "__ref(stg_orders) is not a valid __ref() call", - "models/marts/order_totals.sql", - 4, - 6, - '__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref("stg_orders")', - ), - ), - ), - RejectedReferenceCallTestCase( - description="python engine rejects comment inside ref call", - engine="python", - staging_from=_CANONICAL_SOURCE, - mart_from="__ref( /* upstream */ 'stg_orders')", - expected_diagnostics=( - ( - "P012", - "__ref( /* upstream */ 'stg_orders') is not a valid __ref() call", - "models/marts/order_totals.sql", - 4, - 6, - '__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref("stg_orders")', - ), - ), - ), - RejectedReferenceCallTestCase( - description="python engine rejects single quoted source name", - engine="python", - staging_from="__source('raw_orders')", - mart_from=_CANONICAL_REF, - expected_diagnostics=( - ( - "P012", - "__source('raw_orders') is not a valid __source() call", - "models/staging/stg_orders.sql", - 3, - 36, - '__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source("raw_orders")', - ), - ), - ), - RejectedReferenceCallTestCase( - description="python engine rejects invalid calls in two files", - engine="python", - staging_from="__source('raw_orders')", - mart_from="__ref(stg_orders)", - expected_diagnostics=( - ( - "P012", - "__ref(stg_orders) is not a valid __ref() call", - "models/marts/order_totals.sql", - 4, - 6, - '__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref("stg_orders")', - ), - ( - "P012", - "__source('raw_orders') is not a valid __source() call", - "models/staging/stg_orders.sql", - 3, - 36, - '__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source("raw_orders")', - ), - ), - ), RejectedReferenceCallTestCase( description="native engine rejects unquoted ref name", engine="native", diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/test/test_direct_logic_extraction.py b/tests/e2e/src/sqlbuild/cli/commands/main/test/test_direct_logic_extraction.py index 5c2d4f0ccf..1806b513e0 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/test/test_direct_logic_extraction.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/test/test_direct_logic_extraction.py @@ -222,34 +222,6 @@ @pytest.mark.parametrize( "test_case", [ - DirectLogicExtractionE2ETestCase( - description="macro, UDF and table-function tests pass on python", - engine="python", - overrides={}, - expected_exit_code=0, - expected_output_fragments=_PASSING_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="a quoted CTE name is a located error on python", - engine="python", - overrides=_QUOTED_NAME, - expected_exit_code=1, - expected_output_fragments=_QUOTED_NAME_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="a macro-test helper calling a UDF is rejected on python", - engine="python", - overrides=_HELPER_CALLS_UDF, - expected_exit_code=1, - expected_output_fragments=_HELPER_CALLS_UDF_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="a malformed helper reference is P012 at its call on python", - engine="python", - overrides=_MALFORMED_SOURCE, - expected_exit_code=1, - expected_output_fragments=_MALFORMED_SOURCE_FRAGMENTS, - ), DirectLogicExtractionE2ETestCase( description="macro, UDF and table-function tests pass on native", engine="native", @@ -306,27 +278,6 @@ expected_exit_code=1, expected_output_fragments=_MALFORMED_SOURCE_FRAGMENTS, ), - DirectLogicExtractionE2ETestCase( - description="a CTE name in an indented model test is located exactly on python", - engine="python", - overrides=_LOCATED_MODEL_NAME, - expected_exit_code=1, - expected_output_fragments=_LOCATED_MODEL_NAME_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="a CTE name in a test with cases is located exactly on python", - engine="python", - overrides=_LOCATED_CASE_NAME, - expected_exit_code=1, - expected_output_fragments=_LOCATED_CASE_NAME_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="each repeated malformed reference is P012 at its own call on python", - engine="python", - overrides=_REPEATED_MALFORMED_SOURCE, - expected_exit_code=1, - expected_output_fragments=_REPEATED_MALFORMED_SOURCE_FRAGMENTS, - ), DirectLogicExtractionE2ETestCase( description="a CTE name in an indented model test is located exactly on native", engine="native", @@ -369,20 +320,6 @@ expected_exit_code=1, expected_output_fragments=_REPEATED_MALFORMED_SOURCE_FRAGMENTS, ), - DirectLogicExtractionE2ETestCase( - description="aliased IS [NOT] DISTINCT FROM and list projections pass on python", - engine="python", - overrides=_ALIASED_OPERATORS, - expected_exit_code=0, - expected_output_fragments=_PASSING_FRAGMENTS, - ), - DirectLogicExtractionE2ETestCase( - description="an unaliased IS DISTINCT FROM projection is rejected on python", - engine="python", - overrides=_UNALIASED_DISTINCT_FROM, - expected_exit_code=1, - expected_output_fragments=_UNALIASED_DISTINCT_FROM_FRAGMENTS, - ), DirectLogicExtractionE2ETestCase( description="aliased IS [NOT] DISTINCT FROM and list projections pass on native", engine="native", diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/test/test_helper_references.py b/tests/e2e/src/sqlbuild/cli/commands/main/test/test_helper_references.py index 1e9027c222..09022ef7c6 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/test/test_helper_references.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/test/test_helper_references.py @@ -25,84 +25,6 @@ @pytest.mark.parametrize( "test_case", ( - HelperReferenceE2ETestCase( - description="helper and assertion read the model on the python engine", - tests=("helper_and_assertion_read_model",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=("PASS=1 FAIL=0 TOTAL=1", "assertion doubles_amount"), - expected_compiled_fragments=("\n__helper__doubled AS (",), - expected_absent_compiled_fragments=("__ref(",), - ), - HelperReferenceE2ETestCase( - description="only a helper reads the model on the python engine", - tests=("only_helper_reads_model",), - command=("--no-color", "test", "--select", "item_totals"), - engine="python", - sql_analysis=True, - expected_output_fragments=("1 selected", "PASS=1 FAIL=0 TOTAL=1"), - expected_compiled_fragments=("\n__ref__item_totals AS (",), - expected_absent_compiled_fragments=("__ref(",), - ), - HelperReferenceE2ETestCase( - description="helper reads a helper and a mock on the python engine", - tests=("helper_reads_helper_and_mock",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=( - "PASS=1 FAIL=0 TOTAL=1", - "assertion doubles_mocked_amount", - ), - expected_compiled_fragments=("\n__helper__joined AS (",), - expected_absent_compiled_fragments=("__ref(",), - ), - HelperReferenceE2ETestCase( - description="inlined helpers read by expected rows and assertions on the python engine", - tests=("helper_reads_helper_and_mock",), - command=("--no-color", "test"), - engine="python", - sql_analysis=False, - expected_output_fragments=( - "PASS=1 FAIL=0 TOTAL=1", - "assertion doubles_mocked_amount", - ), - expected_compiled_fragments=( - "(WITH doubled AS (SELECT item_id, amount_doubled FROM __ref__item_totals)", - ), - expected_absent_compiled_fragments=("__ref(", "__helper__"), - ), - HelperReferenceE2ETestCase( - description="helper and assertion read a mocked model on the python engine", - tests=("mocked_model_read_by_helper_and_assertion",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=("PASS=1 FAIL=0 TOTAL=1", "assertion doubles_amount"), - expected_compiled_fragments=("\n__helper__doubled AS (",), - expected_absent_compiled_fragments=("__ref(",), - ), - HelperReferenceE2ETestCase( - description="unread helpers and mocks are ignored on the python engine", - tests=("unread_helpers_and_mocks",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=("PASS=1 FAIL=0 TOTAL=1",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("archived_rows", "other_rows", "__ref("), - ), - HelperReferenceE2ETestCase( - description="unread helpers add no tested models on the python engine", - tests=("unread_helpers_and_mocks",), - command=("--no-color", "test", "--select", "item_extras"), - engine="python", - sql_analysis=True, - expected_output_fragments=("0 selected",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("archived_rows", "other_rows"), - ), HelperReferenceE2ETestCase( description="helper and assertion read the model on the native engine", tests=("helper_and_assertion_read_model",), @@ -181,44 +103,6 @@ expected_compiled_fragments=(), expected_absent_compiled_fragments=("archived_rows", "other_rows"), ), - HelperReferenceE2ETestCase( - description="expected rows and assertion read a mock on the python engine", - tests=("mocked_model_read_by_expected_and_assertion",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=( - "PASS=1 FAIL=0 TOTAL=1", - "assertion reads_mocked_items", - ), - expected_compiled_fragments=( - "SELECT item_id, amount * 2 AS amount_doubled FROM __ref__items", - "SELECT item_id FROM __ref__items WHERE item_id <> 7 OR amount <> 5", - ), - expected_absent_compiled_fragments=( - "__ref(", - "SELECT 1 AS item_id, 10 AS amount", - ), - ), - HelperReferenceE2ETestCase( - description="expected rows and assertion read a mock on the python engine without sql analysis", - tests=("mocked_model_read_by_expected_and_assertion",), - command=("--no-color", "test"), - engine="python", - sql_analysis=False, - expected_output_fragments=( - "PASS=1 FAIL=0 TOTAL=1", - "assertion reads_mocked_items", - ), - expected_compiled_fragments=( - "SELECT item_id, amount * 2 AS amount_doubled FROM __ref__items", - "SELECT item_id FROM __ref__items WHERE item_id <> 7 OR amount <> 5", - ), - expected_absent_compiled_fragments=( - "__ref(", - "SELECT 1 AS item_id, 10 AS amount", - ), - ), HelperReferenceE2ETestCase( description="expected rows and assertion read a mock on the native engine", tests=("mocked_model_read_by_expected_and_assertion",), @@ -295,29 +179,6 @@ "SELECT 1 AS item_id, 10 AS amount", ), ), - HelperReferenceE2ETestCase( - description="selecting a mocked model does not select its test on the python engine", - tests=("mocked_model_read_by_expected_and_assertion",), - command=("--no-color", "test", "--select", "items"), - engine="python", - sql_analysis=True, - expected_output_fragments=("0 selected",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("__ref(",), - ), - HelperReferenceE2ETestCase( - description="expected rows read an unmocked model the test runs on the python engine", - tests=("expected_reads_unmocked_model",), - command=("--no-color", "test", "--select", "item_extras"), - engine="python", - sql_analysis=True, - expected_output_fragments=( - "1 selected", - "PASS=1 FAIL=0 TOTAL=1", - ), - expected_compiled_fragments=("amount * 2 AS amount_doubled FROM __ref__item_extras",), - expected_absent_compiled_fragments=("__ref(",), - ), HelperReferenceE2ETestCase( description="selecting a mocked model does not select its test on the native engine", tests=("mocked_model_read_by_expected_and_assertion",), @@ -364,36 +225,6 @@ expected_compiled_fragments=("amount * 2 AS amount_doubled FROM __ref__item_extras",), expected_absent_compiled_fragments=("__ref(",), ), - HelperReferenceE2ETestCase( - description="unread helper named like a column reads an unknown model on the python engine", - tests=("helper_named_like_column_reads_unknown_model",), - command=("--no-color", "test"), - engine="python", - sql_analysis=True, - expected_output_fragments=("PASS=1 FAIL=0 TOTAL=1",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("__ref(", "item_archive"), - ), - HelperReferenceE2ETestCase( - description="unread helper named like a column reads an unknown model on the python engine without sql analysis", - tests=("helper_named_like_column_reads_unknown_model",), - command=("--no-color", "test"), - engine="python", - sql_analysis=False, - expected_output_fragments=("PASS=1 FAIL=0 TOTAL=1",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("__ref(", "item_archive"), - ), - HelperReferenceE2ETestCase( - description="unread helper named like a column selects no model on the python engine", - tests=("helper_named_like_column_reads_model",), - command=("--no-color", "test", "--select", "item_extras"), - engine="python", - sql_analysis=True, - expected_output_fragments=("0 selected",), - expected_compiled_fragments=(), - expected_absent_compiled_fragments=("__ref(", "__ref__item_extras"), - ), HelperReferenceE2ETestCase( description="unread helper named like a column reads an unknown model on the native engine", tests=("helper_named_like_column_reads_unknown_model",), @@ -497,46 +328,6 @@ def test_given_helper_cte_references_when_testing_then_helpers_read_the_tested_m @pytest.mark.parametrize( "test_case", ( - HelperReferenceErrorE2ETestCase( - description="helpers reading each other on the python engine", - test_name="helper_cycle", - engine="python", - expected_code="P013", - expected_message=( - "SQL test CTE 'first_rows' reads itself through 'first_rows' -> " - "'second_rows' -> 'first_rows', so the test query cannot define its CTEs in " - "dependency order" - ), - expected_line=7, - expected_column=1, - expected_help_fragment="shared_rows AS (SELECT ...)", - ), - HelperReferenceErrorE2ETestCase( - description="helper reading an unknown model on the python engine", - test_name="helper_reads_unknown_model", - engine="python", - expected_code="P013", - expected_message=( - "SQL test helper CTE 'missing_rows' references unknown model 'item_archive'" - ), - expected_line=7, - expected_column=38, - expected_help_fragment='__ref("")', - ), - HelperReferenceErrorE2ETestCase( - description="mock reading a helper that reads a model on the python engine", - test_name="mock_reads_referencing_helper", - engine="python", - expected_code="P013", - expected_message=( - "SQL test mock '__ref__items' reads helper CTE 'base_rows', which calls " - '__ref("item_totals"); mocks and fixtures are defined before the models the ' - "test runs, so the helper cannot be resolved for them" - ), - expected_line=7, - expected_column=35, - expected_help_fragment='FROM __ref__item_totals rather than FROM __ref("item_totals")', - ), HelperReferenceErrorE2ETestCase( description="helpers reading each other on the native engine", test_name="helper_cycle", @@ -577,49 +368,6 @@ def test_given_helper_cte_references_when_testing_then_helpers_read_the_tested_m expected_column=35, expected_help_fragment='FROM __ref__item_totals rather than FROM __ref("item_totals")', ), - HelperReferenceErrorE2ETestCase( - description="assertion reading an unmocked source on the python engine", - test_name="assertion_reads_unmocked_source", - engine="python", - expected_code="P013", - expected_message=( - "SQL test assertion CTE '__assert__no_returns' calls " - '__source("item_returns"), which the test does not mock, so the test query ' - "cannot resolve it" - ), - expected_line=11, - expected_column=23, - expected_help_fragment="__source__item_returns AS (SELECT ...)", - ), - HelperReferenceErrorE2ETestCase( - description="test reading only mocks on the python engine", - test_name="assertion_reads_only_mocks", - engine="python", - expected_code="P013", - expected_message=( - "SQL test 'assertion_reads_only_mocks' mocks the model it tests (__ref__items), " - "so the test has no model to run against" - ), - expected_line=8, - expected_column=23, - expected_help_fragment=( - "Mock the model's inputs instead (for example __source__ or " - '__ref__) and keep __ref("items")' - ), - ), - HelperReferenceErrorE2ETestCase( - description="expected rows reading an unknown model on the python engine", - test_name="expected_reads_unknown_model", - engine="python", - expected_code="P013", - expected_message=( - "SQL test expected CTE '__expected__item_totals' references unknown model " - "'item_archive'" - ), - expected_line=8, - expected_column=39, - expected_help_fragment='__ref("")', - ), HelperReferenceErrorE2ETestCase( description="assertion reading an unmocked source on the native engine", test_name="assertion_reads_unmocked_source", @@ -706,60 +454,6 @@ def test_given_helper_cte_references_when_testing_then_helpers_read_the_tested_m expected_column=39, expected_help_fragment='__ref("")', ), - HelperReferenceErrorE2ETestCase( - description="a mock a check calls reading a referencing helper through a middle helper on the python engine", - test_name="mock_called_reads_referencing_helper", - engine="python", - expected_code="P013", - expected_message=( - "SQL test mock '__source__item_returns' reads helper CTE 'extras', which calls " - '__ref("item_extras"); mocks and fixtures are defined before the models the ' - "test runs, so the helper cannot be resolved for them" - ), - expected_line=4, - expected_column=40, - expected_help_fragment='FROM __ref__item_extras rather than FROM __ref("item_extras")', - ), - HelperReferenceErrorE2ETestCase( - description="a mock a check calls reading a referencing helper directly on the python engine", - test_name="mock_called_reads_referencing_helper_directly", - engine="python", - expected_code="P013", - expected_message=( - "SQL test mock '__source__item_returns' reads helper CTE 'extras', which calls " - '__ref("item_extras"); mocks and fixtures are defined before the models the ' - "test runs, so the helper cannot be resolved for them" - ), - expected_line=4, - expected_column=40, - expected_help_fragment='FROM __ref__item_extras rather than FROM __ref("item_extras")', - ), - HelperReferenceErrorE2ETestCase( - description="test reading a mocked source and a mocked model on the python engine", - test_name="assertion_reads_mocked_source_and_model", - engine="python", - expected_code="P013", - expected_message=( - "SQL test 'assertion_reads_mocked_source_and_model' mocks the model it tests " - "(__ref__items), so the test has no model to run against" - ), - expected_line=10, - expected_column=8, - expected_help_fragment='and keep __ref("items") in the __assert__ or __expected__ CTE', - ), - HelperReferenceErrorE2ETestCase( - description="test reading a mocked model by CTE name on the python engine", - test_name="assertion_reads_mock_by_name", - engine="python", - expected_code="P013", - expected_message=( - "SQL test 'assertion_reads_mock_by_name' mocks the model it tests (__ref__items), " - "so the test has no model to run against" - ), - expected_line=8, - expected_column=23, - expected_help_fragment='and call __ref("items") in the __assert__ or __expected__ CTE', - ), HelperReferenceErrorE2ETestCase( description="a mock a check calls reading a referencing helper through a middle helper on the native engine", test_name="mock_called_reads_referencing_helper", @@ -901,12 +595,6 @@ def test_given_unresolvable_helper_references_when_compiling_then_compile_report @pytest.mark.parametrize( "test_case", ( - HelperRedefinitionE2ETestCase( - description="nested CTE redefining a helper on the python engine", - engine="python", - expected_output_fragments=("error[P001]", "which redefines helper CTE 'doubled'"), - expected_absent_output_fragments=("P013",), - ), HelperRedefinitionE2ETestCase( description="nested CTE redefining a helper on the native engine", engine="native", diff --git a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py index 1fbe015ea9..fa4546e97d 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py @@ -9,14 +9,10 @@ from dataclasses import dataclass from itertools import product -import pytest - from sqlbuild.adapter.contract.models import NormalizedType from sqlbuild.adapter.type_system._helpers import type_normalization from sqlbuild.adapter.type_system.constants import TYPE_NORMALIZATION_LOGGER_NAME from sqlbuild.adapter.type_system.main._native_normalize_type import normalize_native_type -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR -from sqlbuild.compiler.frontier.types import CompilerEngine DIALECTS: tuple[str | None, ...] = ( None, @@ -151,21 +147,18 @@ def generated_type(*, rng: random.Random, depth: int = 0) -> str: return template.format(*children, scalar=_generated_scalar(rng=rng)) -def type_parities(*, type_strings: list[str], monkeypatch: pytest.MonkeyPatch) -> list[TypeParity]: +def type_parities(*, type_strings: list[str]) -> list[TypeParity]: """Normalize every type string under every dialect with Python and natively.""" parities: list[TypeParity] = [] for type_sql, dialect in product(type_strings, DIALECTS): - parities.append(type_parity(type_sql=type_sql, dialect=dialect, monkeypatch=monkeypatch)) + parities.append(type_parity(type_sql=type_sql, dialect=dialect)) return parities -def type_parity( - *, type_sql: str, dialect: str | None, monkeypatch: pytest.MonkeyPatch -) -> TypeParity: - """Normalize one type with the Python engine and with the native type system.""" +def type_parity(*, type_sql: str, dialect: str | None) -> TypeParity: + """Normalize one type with the Python fallback native defers to and natively.""" - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.PYTHON.value) with _logged_errors() as python_errors: python: NormalizedType | str = _python_normalized(type_sql=type_sql, dialect=dialect) with _logged_errors() as native_errors: @@ -199,7 +192,11 @@ def _generated_scalar(*, rng: random.Random) -> str: def _python_normalized(*, type_sql: str, dialect: str | None) -> NormalizedType | str: try: - return type_normalization.normalize_type.__wrapped__(type_sql=type_sql, dialect=dialect) + return type_normalization._normalize_with_polyglot( # noqa: SLF001 + type_sql=type_sql, dialect=dialect + ) or type_normalization._normalize_with_fallback( # noqa: SLF001 + type_sql=type_sql, dialect=dialect + ) except ValueError as error: return f"{type(error).__name__}: {error}" diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py index eb8684d68d..0219ed19b6 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py @@ -51,11 +51,11 @@ ids=lambda case: case.description, ) def test_given_generated_types_when_normalizing_natively_then_python_normalization_matches( - test_case: GeneratedTypeParityTestCase, monkeypatch: pytest.MonkeyPatch + test_case: GeneratedTypeParityTestCase, ) -> None: rng: random.Random = random.Random(test_case.seed) type_strings: list[str] = [generated_type(rng=rng) for _ in range(test_case.count)] - parities: list[TypeParity] = type_parities(type_strings=type_strings, monkeypatch=monkeypatch) + parities: list[TypeParity] = type_parities(type_strings=type_strings) answered: list[TypeParity] = list(filter(is_native, parities)) assert ( @@ -142,11 +142,9 @@ def test_given_generated_types_when_normalizing_natively_then_python_normalizati ids=lambda case: case.description, ) def test_given_type_when_normalizing_under_each_dialect_then_native_answers_match_python( - test_case: TypeParityTestCase, monkeypatch: pytest.MonkeyPatch + test_case: TypeParityTestCase, ) -> None: - parities: list[TypeParity] = type_parities( - type_strings=[test_case.type_sql], monkeypatch=monkeypatch - ) + parities: list[TypeParity] = type_parities(type_strings=[test_case.type_sql]) answered: list[TypeParity] = list(filter(is_native, parities)) assert ( diff --git a/tests/integration/src/sqlbuild/compiler/attachments/_test_types.py b/tests/integration/src/sqlbuild/compiler/attachments/_test_types.py index b63bbf4e49..3ee6f94f54 100644 --- a/tests/integration/src/sqlbuild/compiler/attachments/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/attachments/_test_types.py @@ -1,24 +1,7 @@ from __future__ import annotations -import random -from collections.abc import Callable from dataclasses import dataclass -from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax - - -@dataclass(frozen=True) -class AuthoredSqlParityTestCase: - """Seeded authored SQL expanded by the Python engine and by the preview engine.""" - - description: str - seed: int - count: int - generate: Callable[[random.Random], str] - expected_minimum_expanded: int - expected_minimum_python_errors: int - expected_minimum_exact_errors: int - @dataclass(frozen=True) class AttachedAuditParityTestCase: @@ -32,75 +15,6 @@ class AttachedAuditParityTestCase: expected_minimum_python_errors: int -@dataclass(frozen=True) -class AttachmentProjectTestCase: - """One project variant compiled to compile inputs by each engine.""" - - description: str - overrides: dict[str, str] - expected_native_entries: frozenset[str] - expected_outcome_fragment: str - - -@dataclass(frozen=True) -class ParameterParityTestCase: - """Seeded SQL test bodies whose `@param` references each engine expands.""" - - description: str - seed: int - count: int - expected_minimum_expanded: int - expected_minimum_python_errors: int - expected_minimum_exact_errors: int - - -@dataclass(frozen=True) -class OmittedSelectParityTestCase: - """Seeded test bodies completed with an omitted `SELECT 1` under one lexical syntax.""" - - description: str - seed: int - count: int - syntax: SqlLexicalSyntax - expected_minimum_completed: int - - -@dataclass(frozen=True) -class RawDirectLogicParityTestCase: - """Seeded unexpanded direct-logic test bodies extracted natively and by Python.""" - - description: str - seed: int - count: int - expected_minimum_extracted: int - expected_minimum_python_errors: int - - -@dataclass(frozen=True) -class ScenarioParityTestCase: - """Seeded scenario bodies extracted by each engine under one lexical syntax.""" - - description: str - seed: int - count: int - syntax: SqlLexicalSyntax - expected_minimum_extracted: int - expected_minimum_native: int - expected_minimum_python_errors: int - expected_minimum_native_errors: int - - -@dataclass(frozen=True) -class TargetParityTestCase: - """Seeded model test targets validated by each engine against known resources.""" - - description: str - seed: int - count: int - expected_minimum_valid: int - expected_minimum_python_errors: int - - @dataclass(frozen=True) class BodyCallParityTestCase: """Seeded helper bodies whose call reading the native extractor shares with a Python scanner.""" diff --git a/tests/integration/src/sqlbuild/compiler/attachments/helpers.py b/tests/integration/src/sqlbuild/compiler/attachments/helpers.py index 211c1de33f..5f00772f85 100644 --- a/tests/integration/src/sqlbuild/compiler/attachments/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/attachments/helpers.py @@ -4,17 +4,10 @@ import random import re -from collections.abc import Callable from dataclasses import dataclass -from functools import partial from pathlib import Path from typing import cast -import orjson -import pytest - -import sqlbuild._native as _native -from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.attachments.main._render_native_attached_audit import ( render_native_attached_audit, ) @@ -24,158 +17,16 @@ resolve_audit_run_scope, resolve_audit_severity, ) -from sqlbuild.compiler.compile._helpers.attachment.sql_tests import ( - build_test_inputs, - validate_test_ctes, -) from sqlbuild.compiler.compile._helpers.refs.references import scan_sql_reference_calls -from sqlbuild.compiler.compile._helpers.render import macros from sqlbuild.compiler.compile._helpers.render.arguments import render_parameterized_sql from sqlbuild.compiler.compile._helpers.render.macros import find_macro_call_names -from sqlbuild.compiler.compile._helpers.render.parameters import expand_test_parameters -from sqlbuild.compiler.compile._helpers.render.sql_vars import expand_authored_sql_result -from sqlbuild.compiler.compile._helpers.scenarios.core import extract_sql_scenario_ctes -from sqlbuild.compiler.compile._helpers.sql_tests.core import complete_omitted_ceremonial_select from sqlbuild.compiler.compile._helpers.sql_tests.native import extract_unexpanded_sql_test from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( - AuthoredSqlExpansionResult, - CompileAdapterContext, - CompileModelSqlTestCtes, - CompileProjectInputs, - CompileSqlTestCtes, - DeclarationExpansionContext, - DeclarationResolutionContext, - LoadedMacro, - MacroContext, SqlReferenceScan, ) from sqlbuild.compiler.compile.types import SqlTestMode -from sqlbuild.compiler.discovery.main.discover import discover_project_inputs -from sqlbuild.compiler.discovery.models import ( - DiscoveredProjectInputs, - DiscoveredSqlTestBlock, - DiscoveredSqlTestFile, -) -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR -from sqlbuild.compiler.frontier.main._compile_frontier import compile_frontier -from sqlbuild.compiler.frontier.types import CompilerEngine, CompilerStage from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -from sqlbuild.spec.contracts.main.resolve_effective_collection_rendering import ( - resolve_effective_collection_rendering, -) -from sqlbuild.spec.contracts.models import ( - LocalConfig, - ProjectConfig, -) -from sqlbuild.sql_values.main.normalize import normalize_sql_value -from sqlbuild.sql_values.models import SqlValue -from sqlbuild.sql_values.types import CollectionRendering -from tests.integration.src.sqlbuild.compiler.model_loop.helpers import ( - DECLARATIONS, - generated_reference_sql, -) - -EFFECTIVE_VARS: dict[str, object] = { - "region": "north", - "limit_rows": 10, - "ratio": 0.5, - "enabled": True, - "unset": None, - "regions": ["north", "south"], -} -_VARIABLE_TOKENS: tuple[str, ...] = ( - "@@region", - "@@limit_rows ", - "'@@region'", - "@@ratio", - "@@enabled", - "@@unset", - "@@@window_start", - "@@ENV:SQB_ORDERS_REGION", - "@@ENV:SQB_MISSING_REGION", - "@@regions", - "@@missing", - "@@CTX:run.target", - "-- @@missing\n", -) -_MACRO_CONTEXT: MacroContext = MacroContext( - adapter_name="duckdb", sql_analysis_enabled=True, target_name="dev" -) - - -def generated_authored_sql(*, rng: random.Random) -> str: - """Return SQL mixing project variables with enum and constant references.""" - - parts: list[str] = [ - generated_reference_sql(rng=rng), - *rng.choices(_VARIABLE_TOKENS, k=rng.randint(0, 3)), - ] - rng.shuffle(parts) - return " ".join(parts) - - -_DOLLAR_PIECES: tuple[str, ...] = ( - "$", - "$$", - "$t$", - "$tag$", - "a$", - "$1", - "$1$", - "--", - "/*", - "*/", - "'", - "\n", - " ", - "@@region", - "@@limit_rows", - "@@missing", - "@@ENV:SQB_ORDERS_REGION", - '@const("sales_cap")', - '@enum("order_status").PLACED', - "`", - "``", - "`@@missing``", - "`@@regions``", - "`@@CTX:run.target``", -) - - -def generated_dollar_authored_sql(*, rng: random.Random) -> str: - """Return SQL dense in dollar quotes around variables, enum and constant references.""" - - parts: list[str] = [ - generated_reference_sql(rng=rng), - *rng.choices(_DOLLAR_PIECES, k=rng.randint(1, 10)), - ] - rng.shuffle(parts) - return "".join(parts) - - -def authored_outcome( - *, sql: str, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch -) -> AuthoredSqlExpansionResult | str: - """Expand one string under `engine`, returning the result or Python's error text.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - monkeypatch.setenv("SQB_ORDERS_REGION", "south") - try: - return expand_authored_sql_result( - sql=sql, - file_path=Path("audits/orders.sql"), - effective_vars=EFFECTIVE_VARS, - loaded_macros={}, - macro_context=_MACRO_CONTEXT, - value_renderer=DuckDbAdapter(), - collection_rendering=CollectionRendering.VALUE_LIST, - declarations=DECLARATIONS, - ) - except CompileInputError as error: - return str(error) - _SQL_PIECES: tuple[str, ...] = ( "SELECT * FROM t WHERE ", @@ -357,559 +208,6 @@ def _python_rendering(audit: GeneratedAudit) -> tuple[object, ...] | str: return (*rendered, severity, run_scope) -NATIVE_ATTACHMENT_ENTRIES: tuple[str, ...] = ( - "scan_test_parameter_references", - "omitted_ceremonial_select", - "extract_sql_scenario_json", - "SqlTestTargetCatalog", - "pair_seed_files", - "render_attached_generic_audit", - "expand_config_templates", - "interpolate_sql_batch", - "scan_sql_declaration_references", -) -_MISSING_ENV: str = "SQB_ATTACHMENTS_UNSET" -ATTACHMENT_PROJECT: dict[str, str] = { - "sqlbuild_project.toml": ( - 'name = "orders"\nadapter = "duckdb"\ndefault_target = "dev"\n\n' - '[vars]\nregion = "north"\n\n' - '[connections.local]\ndatabase = "orders.duckdb"\n\n' - '[targets.dev]\nconnection = "local"\nschema = "dev"\n' - ), - "macros/labels.py": ( - "def tidy_label(expression: str) -> str:\n" - ' """Trim and lower-case a label."""\n' - ' return f"LOWER(TRIM({expression}))"\n' - ), - "seeds/channel_codes.csv": "id,label\n1,web\n2,store\n", - "seeds/channel_codes.yml": ( - "seeds:\n - name: channel_codes\n description: Channel codes.\n columns:\n" - " - name: id\n type: INTEGER\n audits:\n - not_null\n" - " - name: label\n type: VARCHAR\n" - ), - "seeds/product_codes.csv": "id\n1\n", - "seeds/product_codes.yml": ( - "seeds:\n - name: product_codes\n columns:\n - name: id\n type: INTEGER\n" - ), - "sources/events.yml": ( - "sources:\n - name: order_events\n" - f" description: \"Order feed ${{coalesce(ENV:{_MISSING_ENV}, 'events')}}\"\n" - " expression: \"(SELECT 1 AS id, @tidy_label('@@region') AS region, " - '$$--@@region /* $$ AS note)"\n' - " columns:\n - name: id\n type: INTEGER\n audits:\n" - " - accepted_values:\n values: [1, 2]\n" - " - name: region\n type: VARCHAR\n" - ), - "functions/sql/order_label.sql": ( - 'FUNCTION (\n description "Label an order status.",\n' - f" schema \"${{coalesce(ENV:{_MISSING_ENV}, 'udfs')}}\",\n" - " arguments (raw_status STRING),\n returns STRING,\n);\n\n" - 'UPPER(@tidy_label("raw_status"))\n' - ), - "audits/generic/amount_floor.sql": ( - 'AUDIT ();\n\nSELECT *\nFROM __ref("@model")\nWHERE @column < @minimum ' - "AND @tidy_label(\"label\") <> @'label' AND $tag$--@@region$tag$ <> @'label'\n" - ), - "models/orders.sql": ( - 'MODEL (\n materialized table,\n description "Orders.",\n' - ' audits [amount_floor (column amount, minimum -5, label "it\'s", severity warn)],\n' - ");\n\nSELECT c.id, 1.5 AS amount, c.label\n" - 'FROM __seed("channel_codes") c\nJOIN __source("order_events") e ON e.id = c.id\n' - ), - "tests/unit/test_orders.sql": ( - "TEST();\n\nWITH\n__seed__channel_codes AS (\n" - " SELECT 1 AS id, @tidy_label(\"' Web '\") AS label, $$ /* $$ AS note, " - "'@@region' AS region -- */\n),\n" - "__source__order_events AS (\n SELECT 1 AS id, 'north' AS region\n),\n" - "__expected__orders AS (\n SELECT 1 AS id, 1.5 AS amount, 'web' AS label\n)\n" - "SELECT 1\n" - ), - "tests/unit/test_orders_cases.sql": ( - 'TEST (\n name "orders_keep_region",\n parameters (\n region_value string,\n ),\n' - ' cases (\n north (region_value "north"),\n' - ' quoted (region_value "it\'s"),\n ),\n);\n\n' - "WITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - '__source__order_events AS (SELECT 1 AS id, @param("region_value") AS region),\n' - "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label);\n" - ), - "tests/unit/test_tidy_label.sql": ( - 'TEST (mode macro, name "tidies_labels");\n\n' - "WITH\ninput_values AS (SELECT ' Web ' AS label),\n" - '__macro_actual__ AS (SELECT @tidy_label("label") AS label FROM input_values),\n' - "__macro_expected__ AS (SELECT 'web' AS label)\nSELECT 1\n" - ), - "tests/scenarios/orders_scenario.sql": ( - 'SCENARIO (\n description "Orders join their channel"\n);\n\n' - "WITH\n__seed__channel_codes AS (\n SELECT 1 AS id, @tidy_label(\"'Web'\") AS label\n),\n" - "__source__order_events AS (\n SELECT 1 AS id, 'north' AS region\n),\n" - "__expected__orders AS (\n SELECT 1 AS id, 1.5 AS amount, 'web' AS label\n)\n" - "SELECT 1\n" - ), -} - - -def attachment_engine_outcome( - *, - project_dir: Path, - engine: CompilerEngine, - overrides: dict[str, str], - monkeypatch: pytest.MonkeyPatch, -) -> tuple[frozenset[str], str]: - """Build compile inputs under `engine`; return the native entries called and the outcome.""" - - for relative_path, contents in {**ATTACHMENT_PROJECT, **overrides}.items(): - path: Path = project_dir / relative_path - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(contents, encoding="utf-8") - called: set[str] = set() - for name in NATIVE_ATTACHMENT_ENTRIES: - monkeypatch.setattr(_native, name, _recorded(called=called, name=name)) - bridged: list[Path] = [] - monkeypatch.setattr(macros, "_bridged_sql_macros", _recorded_bridge(bridged=bridged)) - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - try: - inputs: CompileProjectInputs = compile_frontier( - until=CompilerStage.COMPILE_PROJECT_INPUTS, - python_stage=partial(_compile_inputs, project_dir=project_dir), - ) - except CompileInputError as error: - return frozenset(called), f"error: {error}".replace(str(project_dir), "") - called.update(f"bridged:{str(path).replace(str(project_dir), '')}" for path in bridged) - return frozenset(called), repr( - ( - inputs.seed_inputs, - inputs.source_inputs, - inputs.sql_function_inputs, - inputs.audit_inputs, - inputs.test_inputs, - inputs.scenario_inputs, - inputs.diagnostics, - ) - ).replace(str(project_dir), "") - - -def _compile_inputs(*, project_dir: Path) -> CompileProjectInputs: - discovered: DiscoveredProjectInputs = discover_project_inputs(project_dir=project_dir) - adapter: DuckDbAdapter = DuckDbAdapter() - return build_compile_inputs( - discovered_inputs=discovered, - adapter_context=CompileAdapterContext( - value_renderer=adapter, - collection_rendering=resolve_effective_collection_rendering( - project_config=discovered.project_config, declaration_override=None - ), - python_functions_inherit_default_namespace=( - adapter.python_functions_inherit_default_namespace() - ), - sql_lexical_syntax=adapter.sql_lexical_syntax, - ), - run_id="20261008T000000Z_orders", - resolved_connection={}, - no_sql_validation=True, - defer_model_sql_validation=True, - no_cache=True, - ) - - -def _recorded_bridge(*, bridged: list[Path]) -> Callable[..., object]: - entry: Callable[..., object] = cast(Callable[..., object], macros._bridged_sql_macros) - - def recorded(*, consumer_path: Path, **arguments: object) -> object: - bridged.append(consumer_path) - return entry(consumer_path=consumer_path, **arguments) - - return recorded - - -def _recorded(*, called: set[str], name: str) -> Callable[..., object]: - entry: Callable[..., object] = getattr(_native, name) - - def recorded(*args: object) -> object: - called.add(name) - return entry(*args) - - return recorded - - -_PARAMETER_PIECES: tuple[str, ...] = ( - "SELECT ", - '@param("region")', - '@param ( "limit_rows" )', - '@param(\n"region"\n)', - '@param(\u00a0"region")', - '@param("missing")', - "@param(region)", - "@params", - "@param_x", - "'@param(\"region\")'", - '"quoted"', - "`tick`", - '$$ @param("region") $$', - "$1", - '-- @param("region")\n', - '/* @param("region") */', - "'", - "/*", - "\u00e9", - ", ", -) -PARAMETER_VALUES: tuple[tuple[str, SqlValue], ...] = ( - ("region", normalize_sql_value(raw_value="north", context="region")), - ("limit_rows", normalize_sql_value(raw_value=10, context="limit_rows")), -) - - -def generated_parameter_sql(*, rng: random.Random) -> str: - """Return a test body mixing parameter references with quotes and comments.""" - - return "".join(rng.choices(_PARAMETER_PIECES, k=rng.randint(1, 8))) - - -def parameter_outcome( - *, sql: str, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch -) -> tuple[str, frozenset[str]] | str: - """Expand one body's parameters under `engine`, returning Python's error text on failure.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - try: - return expand_test_parameters( - sql=sql, - file_path=Path("tests/unit/test_orders.sql"), - values=PARAMETER_VALUES, - value_renderer=DuckDbAdapter(), - test_name="orders", - case_name="north", - ) - except CompileInputError as error: - return str(error) - - -_BODY_PIECES: tuple[str, ...] = ( - "WITH ", - "with ", - "WITHIN ", - "w\u0131th ", - "__seed__orders AS (SELECT 1)", - "a AS (SELECT ')' AS x)", - "b AS (SELECT (1))", - ", ", - ")", - "(", - " SELECT 1", - ";", - "\n", - " -- done", - " # done", - " /* done */", - "/* /* nested */ */", - "'", - "'it\\'s'", - "'''tri)ple'''", - "$$)$$", - "$1", - "\u00e9", - "\v", -) - - -def generated_test_body(*, rng: random.Random) -> str: - """Return a test or scenario body around CTEs, terminators, comments and quotes.""" - - keyword: str = rng.choice(_BODY_PIECES[:4]) - ctes: str = ", ".join(rng.choices(_BODY_PIECES[4:7], k=rng.randint(1, 3))) - return keyword + ctes + "".join(rng.choices(_BODY_PIECES, k=rng.randint(0, 3))) - - -def completed_body( - *, - sql: str, - syntax: SqlLexicalSyntax, - engine: CompilerEngine, - monkeypatch: pytest.MonkeyPatch, -) -> str: - """Complete one body's omitted `SELECT 1` under `engine`.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - return complete_omitted_ceremonial_select(sql=sql, syntax=syntax) - - -_RAW_MODE_CTES: dict[SqlTestMode, tuple[str, str]] = { - SqlTestMode.MACRO: ("__macro_actual__", "__macro_expected__"), - SqlTestMode.UDF: ("__udf_actual__", "__udf_expected__"), - SqlTestMode.TABLE_FN: ("__table_fn_actual__", "__table_fn_expected__"), -} -_RAW_CALLS: tuple[str, ...] = ( - "", - "@tidy_label(1)", - "@MY_MACRO(1)", - '__udf("order_label")', - '__udf ("order_label")', - "__udf('order_label')", - '__udf("order_label", 1)', - '__table_fn("order_rows")', - "'__udf(\"order_label\")'", - '-- __udf("order_label")\n', - "/* @tidy_label(1) */", - '$$__udf("order_label")$$', - "@m()", - "@@region", - '__ref("orders")', - '__source("events", "orders")', - '__udf("\u00e9")', - '`__udf("order_label")`', - '__udf\u00a0("order_label")', - '\u00e9__udf("order_label")', -) -_RAW_CTE_NAMES: tuple[str, ...] = ( - "helper", - "a\u00e9", - '"quoted"', - "`ticked`", - "a$b", - "__ref__orders", - "__expected__orders", - "__macro__tidy", - "__udf_actual__", - "__udf_expected__", - "__macro_expected__", - "__table_fn_expected__", - "HELPER", - "\u0131", - "\u017f", -) -_RAW_TAILS: tuple[str, ...] = ("", " FROM helper", " -- c", " UNION ALL SELECT 2", ", 'x' AS b") - - -def _raw_body(rng: random.Random) -> str: - call: str = rng.choice(("1", "1", "1", "1", *_RAW_CALLS)) - projection: str = rng.choice((f"SELECT {call} AS a", f"SELECT {call} AS a", f"SELECT {call}")) - return projection + rng.choice(("", "", "", *_RAW_TAILS)) - - -def generated_raw_direct_logic_test(*, rng: random.Random) -> tuple[str, SqlTestMode]: - """Return one unexpanded direct-logic test with the shapes Python and native read apart.""" - - mode: SqlTestMode = rng.choice(tuple(_RAW_MODE_CTES)) - actual, expected = _RAW_MODE_CTES[mode] - ctes: list[tuple[str, str]] = [ - (actual, _raw_body(rng)), - (expected, _raw_body(rng)), - *((rng.choice(_RAW_CTE_NAMES), _raw_body(rng)) for _ in range(rng.choice((0, 0, 1, 2)))), - ] - rng.shuffle(ctes) - body: str = ", ".join(f"{name} AS ({sql})" for name, sql in ctes) - return f"WITH {body}" + rng.choice(("", "", " SELECT 1", ";")), mode - - -def raw_extraction_accepts(*, sql: str, mode: SqlTestMode) -> bool: - """Whether the raw direct-logic pass accepts `sql` before expansion.""" - - try: - _ = extract_unexpanded_sql_test( - sql=sql, - file_label="tests/unit/test_logic.sql", - syntax=DuckDbAdapter().sql_lexical_syntax, - mode=mode, - ) - except CompileInputError: - return False - return True - - -def raw_test_compile_outcome( - *, sql: str, mode: SqlTestMode, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch -) -> str: - """Build one unexpanded direct-logic test's input under `engine`, or Python's error text.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - adapter: DuckDbAdapter = DuckDbAdapter() - try: - return repr( - build_test_inputs( - discovered_inputs=DiscoveredProjectInputs( - project_config=ProjectConfig(name="orders", adapter="duckdb"), - local_config=LocalConfig(), - test_files=( - DiscoveredSqlTestFile( - file_path=Path("/project/tests/unit/test_logic.sql"), - relative_path=Path("tests/unit/test_logic.sql"), - contents=sql, - blocks=( - DiscoveredSqlTestBlock( - test_index=0, header_values={}, sql_body=sql, mode=mode - ), - ), - ), - ), - ), - effective_vars={"region": "north"}, - macro_context=_MACRO_CONTEXT, - loaded_macros={}, - declaration_expansion=DeclarationExpansionContext( - declarations=DeclarationResolutionContext(), - value_renderer=adapter, - collection_rendering=CollectionRendering.VALUE_LIST, - ), - sql_lexical_syntax=adapter.sql_lexical_syntax, - ) - ) - except CompileInputError as error: - return f"error: {error}" - - -_SCENARIO_CTES: tuple[str, ...] = ( - "__source__raw_orders AS (SELECT 1 AS id, 'placed' AS status)", - "__ref__customers AS (SELECT 1 AS id)", - "__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label)", - "__dbt_ref__legacy__orders AS (SELECT 1 AS id)", - "__table_fn__order_rows AS (SELECT 1 AS id)", - "helper AS (SELECT id FROM __source__raw_orders)", - "__expected__orders AS (SELECT 1 AS id)", - "__expected__orders_view AS (SELECT id FROM helper)", - "__assert__positive AS (SELECT id FROM helper WHERE id < 0)", - "__assert__matches AS (SELECT * FROM __expected__orders)", - "__expected__nested AS (WITH __assert__inner AS (SELECT 1) SELECT 1 AS id)", - "__macro__tidy AS (SELECT 'x')", - '"__source__quoted" AS (SELECT 1)', - "__expected__ AS (SELECT 1)", - "__source__raw_orders AS (SELECT 2 AS id)", - "notes AS (SELECT 'caf\u00e9 -- )' AS note /* ) */)", - "dollar AS (SELECT $$ ) $$ AS note)", - "hashed AS (SELECT 1 # )\n)", -) -_SCENARIO_ERROR_CTES: tuple[str, ...] = ( - "__ref__ AS (SELECT 1)", - "__seed__ AS (SELECT 1)", - "__dbt_ref__ AS (SELECT 1)", - "__assert__ AS (SELECT 1)", - "missing_as (SELECT 1)", - "no_body AS SELECT 1", - "columns (id, label) AS (SELECT 1, 'x')", - "unclosed AS (SELECT (1)", - "open_quote AS (SELECT 'x)", - "/* open AS (SELECT 1)", - "__assert__reads_expected AS (SELECT id FROM __expected__orders_view)", - "caf\u00e9 AS (SELECT 1)", -) -_SCENARIO_ERROR_WEIGHTS: tuple[int, int] = (3, 1) -_DEFERRED_ANSWER: str = '{"deferred": true}' -_NATIVE_SCENARIO_ANSWERS: dict[frozenset[str], str] = { - frozenset({"deferred"}): "deferred", - frozenset({"independence", "scenario"}): "independence", - frozenset({"independence", "error"}): "independence", - frozenset({"error"}): "error", - frozenset({"scenario"}): "extracted", -} -_SCENARIO_TAILS: tuple[str, ...] = ("\nSELECT 1\n", "", ";", "\nSELECT 2", " -- end", ";\n") -_SCENARIO_KEYWORDS: tuple[str, ...] = ( - "WITH\n", - "with ", - "WITH RECURSIVE ", - "w\u0131th ", - "SELECT ", -) - - -def generated_scenario(*, rng: random.Random) -> str: - """Return one scenario body mixing fixtures, checks, helpers and invalid CTEs.""" - - ctes: list[str] = rng.sample(_SCENARIO_CTES, k=rng.randint(1, 5)) - errors: list[str] = rng.sample( - _SCENARIO_ERROR_CTES, k=rng.choices((0, 1), weights=_SCENARIO_ERROR_WEIGHTS)[0] - ) - mixed: list[str] = rng.sample([*ctes, *errors], k=len(ctes) + len(errors)) - return rng.choice(_SCENARIO_KEYWORDS) + ",\n".join(mixed) + rng.choice(_SCENARIO_TAILS) - - -def scenario_outcome( - *, - sql: str, - syntax: SqlLexicalSyntax, - engine: CompilerEngine, - monkeypatch: pytest.MonkeyPatch, -) -> object: - """Extract one scenario under `engine`, or return Python's error text.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - try: - return extract_sql_scenario_ctes( - sql=sql, file_label="tests/scenarios/orders.sql", syntax=syntax - ) - except CompileInputError as error: - return str(error) - - -def native_scenario_answer(*, sql: str, syntax: SqlLexicalSyntax) -> str: - """How the native extraction answers: extracted, error, or the Python step it leaves.""" - - response: str | None = _native.extract_sql_scenario_json( - sql, "tests/scenarios/orders.sql", syntax.native_mapping - ) - outcome: dict[str, object] = orjson.loads(response or _DEFERRED_ANSWER) - return _NATIVE_SCENARIO_ANSWERS[frozenset(outcome)] - - -_TARGET_NAMES: tuple[str, ...] = ("orders", "customers", "channel_codes", "returns", "order_rows") -_KNOWN_NAMES: frozenset[str] = frozenset({"orders", "customers", "channel_codes", "order_rows"}) - - -def generated_test_targets( - *, rng: random.Random -) -> tuple[CompileModelSqlTestCtes, tuple[str, ...]]: - """Return one model test payload and assertion targets mixing known and unknown names.""" - - def names() -> tuple[str, ...]: - return tuple(rng.sample(_TARGET_NAMES, k=rng.choice((0, 0, 1, 1, 2)))) - - return ( - CompileModelSqlTestCtes( - macro_mocks=dict.fromkeys(names(), "'x'"), - mock_model_names=names(), - mock_source_names=names(), - mock_seed_names=names(), - mock_table_function_names=names(), - expected_model_names=names(), - ), - names(), - ) - - -def target_validation_outcome( - *, - payload: CompileModelSqlTestCtes, - assertion_targets: tuple[str, ...], - engine: CompilerEngine, - monkeypatch: pytest.MonkeyPatch, -) -> str | None: - """Validate one payload's targets under `engine`, returning Python's error text.""" - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - known: set[str] = set(_KNOWN_NAMES) - try: - validate_test_ctes( - test_ctes=CompileSqlTestCtes(mode=SqlTestMode.MODEL, payload=payload), - test_file=DiscoveredSqlTestFile( - file_path=Path("/project/tests/unit/test_orders.sql"), - relative_path=Path("tests/unit/test_orders.sql"), - contents="", - blocks=(), - ), - known_model_names=known, - known_seed_names=known, - known_source_names=known, - known_table_function_names=known, - loaded_macros=dict.fromkeys(known, cast(LoadedMacro, None)), - assertion_target_model_names=assertion_targets, - ) - except CompileInputError as error: - return str(error) - return None - - -_NAMES: tuple[object, ...] = ("raw_status", " amount ", "", " ", 1) -_TEXTS: tuple[object, ...] = ("orders", " Orders. ", "", " ", 3, None) - - _MACRO_PIECES: tuple[str, ...] = ( "a", "@m(1)", diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_attachment_project.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_attachment_project.py deleted file mode 100644 index c9eca73ebe..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_attachment_project.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Every attachment kind compiles like Python natively, with macros through the bridge.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from sqlbuild.compiler.frontier.types import CompilerEngine -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - AttachmentProjectTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - attachment_engine_outcome, -) - -_BRIDGED_CONSUMERS: frozenset[str] = frozenset( - { - "bridged:/audits/generic/accepted_values.sql", - "bridged:/audits/generic/not_null.sql", - "bridged:/audits/generic/amount_floor.sql", - "bridged:/functions/sql/order_label.sql", - "bridged:/models/orders.sql", - "bridged:/sources/events.yml", - "bridged:/tests/scenarios/orders_scenario.sql", - "bridged:/tests/unit/test_orders.sql", - } -) -_NATIVE_ONLY_ENTRIES: frozenset[str] = ( - frozenset( - { - "pair_seed_files", - "scan_test_parameter_references", - "SqlTestTargetCatalog", - } - ) - | _BRIDGED_CONSUMERS -) -_ATTACHMENT_ENTRIES: frozenset[str] = frozenset( - { - "pair_seed_files", - "render_attached_generic_audit", - "expand_config_templates", - "interpolate_sql_batch", - "scan_sql_declaration_references", - "scan_test_parameter_references", - "omitted_ceremonial_select", - "extract_sql_scenario_json", - "SqlTestTargetCatalog", - } -) -_SCENARIO: str = "tests/scenarios/orders_scenario.sql" -_SCENARIO_HEADER: str = 'SCENARIO (\n description "Orders join their channel"\n);\n\n' -_UNIT_TEST: str = "tests/unit/test_orders.sql" -_HELPER_TEST_MOCKS: str = ( - "TEST();\n\nWITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - "__source__order_events AS (SELECT 1 AS id, 'north' AS region),\n" -) -_HELPER_TEST_EXPECTED: str = "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label)" - - -@pytest.mark.parametrize( - "test_case", - [ - AttachmentProjectTestCase( - description="macros in every attachment kind, seeds, templates and generic audits", - overrides={}, - expected_native_entries=_ATTACHMENT_ENTRIES | _BRIDGED_CONSUMERS, - expected_outcome_fragment="((CompileSeedInput(", - ), - AttachmentProjectTestCase( - description="a scenario check CTE reading a project source", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + "WITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + '__expected__orders AS (SELECT id, 1.5 AS amount, label FROM __source("order_events"))\n' - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario file tests/scenarios/orders_scenario.sql CTE", - ), - AttachmentProjectTestCase( - description="a scenario helper reading an unknown source", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + 'WITH\nhelper AS (SELECT * FROM __source("missing_events")),\n' - + "__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario file tests/scenarios/orders_scenario.sql " - "references unknown source 'missing_events'", - ), - AttachmentProjectTestCase( - description="a check CTE reading a source before a later malformed reference call", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + "WITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + '__expected__orders AS (SELECT id, 1.5 AS amount, label FROM __source("order_events")),\n' - + "__assert__no_rows AS (SELECT 1 FROM __source(order_events))\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario file tests/scenarios/orders_scenario.sql CTE " - "'__expected__orders' must not reference project source", - ), - AttachmentProjectTestCase( - description="a malformed call in a check CTE leaves the unknown helper source first", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + 'WITH\nhelper AS (SELECT * FROM __source("missing_events")),\n' - + "__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + "__expected__orders AS (SELECT 1 AS id FROM __source(order_events))\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario file tests/scenarios/orders_scenario.sql " - "references unknown source 'missing_events'", - ), - AttachmentProjectTestCase( - description="an unknown helper source before a later malformed fixture call", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + 'WITH\nhelper AS (SELECT * FROM __source("missing_events")),\n' - + "__seed__channel_codes AS (SELECT 1 AS id FROM __source(order_events)),\n" - + "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario file tests/scenarios/orders_scenario.sql " - "references unknown source 'missing_events'", - ), - AttachmentProjectTestCase( - description="a scenario fixture without a target name", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + "WITH\n__seed__ AS (SELECT 1 AS id, 'web' AS label),\n" - + "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL scenario 'tests/scenarios/orders_scenario.sql' " - "must use __seed__ to identify a target", - ), - AttachmentProjectTestCase( - description="a scenario macro mock", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + "WITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + "__macro__tidy AS (SELECT 'x'),\n" - + "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="does not support macro mock CTE '__macro__tidy'", - ), - AttachmentProjectTestCase( - description="a scenario whose expected result depends on an assertion", - overrides={ - _SCENARIO: _SCENARIO_HEADER - + "WITH\n__seed__channel_codes AS (SELECT 1 AS id, 'web' AS label),\n" - + "__assert__no_rows AS (SELECT 1 FROM __seed__channel_codes WHERE id IS NULL),\n" - + "__expected__orders AS (SELECT 1 AS id, 1.5 AS amount, 'web' AS label " - + "FROM __assert__no_rows)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="check CTE '__expected__orders' must not depend on " - "'__assert__no_rows'", - ), - AttachmentProjectTestCase( - description="a helper CTE reading a model through __ref and read by an assertion", - overrides={ - _UNIT_TEST: _HELPER_TEST_MOCKS - + 'built_orders AS (SELECT id FROM __ref("orders")),\n' - + _HELPER_TEST_EXPECTED - + ",\n__assert__has_rows AS (SELECT 1 FROM built_orders WHERE id IS NULL)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="reference_target_model_names=('orders',)", - ), - AttachmentProjectTestCase( - description="a helper chain where one helper reads another", - overrides={ - _UNIT_TEST: _HELPER_TEST_MOCKS - + 'built_orders AS (SELECT id FROM __ref("orders")),\n' - + "built_ids AS (SELECT id FROM built_orders),\n" - + _HELPER_TEST_EXPECTED - + ",\n__assert__has_ids AS (SELECT 1 FROM built_ids WHERE id IS NULL)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="reference_target_model_names=('orders',)", - ), - AttachmentProjectTestCase( - description="an unread helper referencing an unknown model", - overrides={ - _UNIT_TEST: _HELPER_TEST_MOCKS - + 'unused_returns AS (SELECT id FROM __ref("returns")),\n' - + _HELPER_TEST_EXPECTED - + "\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="read_helper_names=(), reference_target_model_names=()", - ), - AttachmentProjectTestCase( - description="a read helper referencing an unknown model reports P013", - overrides={ - _UNIT_TEST: _HELPER_TEST_MOCKS - + 'built_returns AS (SELECT id FROM __ref("returns")),\n' - + _HELPER_TEST_EXPECTED - + ",\n__assert__no_returns AS (SELECT 1 FROM built_returns)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="P013", - ), - AttachmentProjectTestCase( - description="a model test expecting an unknown model", - overrides={ - _UNIT_TEST: "TEST();\n\nWITH\n__seed__channel_codes AS (SELECT 1 AS id),\n" - "__expected__returns AS (SELECT 1 AS id)\n" - }, - expected_native_entries=frozenset({"SqlTestTargetCatalog"}), - expected_outcome_fragment="error: SQL test file tests/unit/test_orders.sql expects " - "unknown model 'returns'", - ), - ], - ids=lambda case: case.description, -) -def test_given_attachment_project_when_building_inputs_then_native_engines_match_python( - test_case: AttachmentProjectTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - outcomes: dict[CompilerEngine, tuple[frozenset[str], str]] = { - engine: attachment_engine_outcome( - project_dir=tmp_path / engine.value, - engine=engine, - overrides=test_case.overrides, - monkeypatch=monkeypatch, - ) - for engine in CompilerEngine - } - python_called, python_outcome = outcomes[CompilerEngine.PYTHON] - native_called, native_outcome = outcomes[CompilerEngine.NATIVE] - preview_called, preview_outcome = outcomes[CompilerEngine.NATIVE_PREVIEW] - - assert ( - preview_outcome.replace(CompilerEngine.NATIVE_PREVIEW.value, "python"), - native_outcome.replace(CompilerEngine.NATIVE.value, "python"), - test_case.expected_outcome_fragment in python_outcome, - native_called >= test_case.expected_native_entries, - preview_called >= test_case.expected_native_entries, - python_called & _NATIVE_ONLY_ENTRIES, - ) == (python_outcome, python_outcome, True, True, True, frozenset()), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_authored_sql.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_authored_sql.py deleted file mode 100644 index a8863d30da..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_authored_sql.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Authored SQL outside models expands identically under the preview engine.""" - -from __future__ import annotations - -import random - -import pytest - -from sqlbuild.compiler.compile.models import AuthoredSqlExpansionResult -from sqlbuild.compiler.frontier.types import CompilerEngine -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - AuthoredSqlParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - authored_outcome, - generated_authored_sql, - generated_dollar_authored_sql, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - AuthoredSqlParityTestCase( - description="seeded variables, enum and constant references, quotes and comments", - seed=20261008, - count=2000, - generate=lambda rng: generated_authored_sql(rng=rng), - expected_minimum_expanded=600, - expected_minimum_python_errors=300, - expected_minimum_exact_errors=300, - ), - AuthoredSqlParityTestCase( - description="dollar quotes around variables, enum and constant references", - seed=550, - count=2000, - generate=lambda rng: generated_dollar_authored_sql(rng=rng), - expected_minimum_expanded=300, - expected_minimum_python_errors=300, - expected_minimum_exact_errors=600, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_authored_sql_when_expanding_with_preview_then_python_output_matches( - test_case: AuthoredSqlParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - sqls: list[str] = [test_case.generate(rng) for _ in range(test_case.count)] - - python: list[AuthoredSqlExpansionResult | str] = [ - authored_outcome(sql=sql, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch) - for sql in sqls - ] - preview: list[AuthoredSqlExpansionResult | str] = [ - authored_outcome(sql=sql, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch) - for sql in sqls - ] - - assert ( - mismatches(inputs=[*sqls], expected=[*python], actual=[*preview]), - sum(isinstance(item, AuthoredSqlExpansionResult) for item in python) - >= test_case.expected_minimum_expanded, - sum(isinstance(item, str) for item in python) >= test_case.expected_minimum_python_errors, - sum(isinstance(item, str) for item in preview) >= test_case.expected_minimum_exact_errors, - ) == ([], True, True, True), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_omitted_select.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_omitted_select.py deleted file mode 100644 index 7fa88f1c09..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_omitted_select.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Test and scenario bodies gain an omitted `SELECT 1` identically under the preview engine.""" - -from __future__ import annotations - -import random - -import pytest - -from sqlbuild.compiler.frontier.types import CompilerEngine -from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - OmittedSelectParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - completed_body, - generated_test_body, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - OmittedSelectParityTestCase( - description="generic syntax", - seed=20261008, - count=4000, - syntax=SqlLexicalSyntax(), - expected_minimum_completed=1000, - ), - OmittedSelectParityTestCase( - description="backslash escapes, raw and triple-quoted strings, hash comments", - seed=20261009, - count=4000, - syntax=SqlLexicalSyntax( - backslash_escape_quotes=frozenset({"'", '"'}), - raw_string_prefix=True, - triple_quoted_strings=True, - line_comment_prefixes=frozenset({"--", "#"}), - ), - expected_minimum_completed=1000, - ), - OmittedSelectParityTestCase( - description="escape strings and nested block comments", - seed=20261010, - count=4000, - syntax=SqlLexicalSyntax(escape_string_prefix=True, nested_block_comments=True), - expected_minimum_completed=1000, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_bodies_when_completing_select_then_python_output_matches( - test_case: OmittedSelectParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - sqls: list[str] = [generated_test_body(rng=rng) for _ in range(test_case.count)] - - python: list[str] = [ - completed_body( - sql=sql, syntax=test_case.syntax, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch - ) - for sql in sqls - ] - preview: list[str] = [ - completed_body( - sql=sql, - syntax=test_case.syntax, - engine=CompilerEngine.NATIVE_PREVIEW, - monkeypatch=monkeypatch, - ) - for sql in sqls - ] - - assert ( - mismatches(inputs=[*sqls], expected=[*python], actual=[*preview]), - sum(map(str.__ne__, python, sqls)) >= test_case.expected_minimum_completed, - ) == ([], True), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_raw_direct_logic_tests.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_raw_direct_logic_tests.py deleted file mode 100644 index 4d0a156133..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_raw_direct_logic_tests.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Unexpanded direct-logic SQL tests build identical inputs under every compiler engine. - -Every engine extracts tests before expansion with the native extractor, so quoted CTE names, -calls in `__macro_expected__` and malformed reference calls are rejected the same way. -""" - -from __future__ import annotations - -import random - -import pytest - -from sqlbuild.compiler.compile.types import SqlTestMode -from sqlbuild.compiler.frontier.types import CompilerEngine -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - RawDirectLogicParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - generated_raw_direct_logic_test, - raw_extraction_accepts, - raw_test_compile_outcome, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - RawDirectLogicParityTestCase( - description="quoted, non-ASCII and $ CTE names, calls in expected CTEs, bad reference calls", - seed=20261008, - count=3000, - expected_minimum_extracted=400, - expected_minimum_python_errors=600, - ), - ], - ids=lambda case: case.description, -) -def test_given_unexpanded_direct_logic_tests_when_compiling_in_preview_then_python_matches( - test_case: RawDirectLogicParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - tests: list[tuple[str, SqlTestMode]] = [ - generated_raw_direct_logic_test(rng=rng) for _ in range(test_case.count) - ] - - python: list[str] = [ - raw_test_compile_outcome( - sql=sql, mode=mode, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch - ) - for sql, mode in tests - ] - native: list[str] = [ - raw_test_compile_outcome( - sql=sql, mode=mode, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch - ) - for sql, mode in tests - ] - - assert ( - mismatches(inputs=[*tests], expected=[*python], actual=[*native]), - sum(raw_extraction_accepts(sql=sql, mode=mode) for sql, mode in tests) - >= test_case.expected_minimum_extracted, - sum(item.startswith("error: ") for item in python) - >= test_case.expected_minimum_python_errors, - ) == ([], True, True), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_scenario_extraction.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_scenario_extraction.py deleted file mode 100644 index 9ac53d537d..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_scenario_extraction.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Scenario CTEs and errors extract identically under the preview engine, mostly natively.""" - -from __future__ import annotations - -import random -from collections import Counter - -import pytest - -from sqlbuild.compiler.compile.models import CompileSqlScenarioCtes -from sqlbuild.compiler.frontier.types import CompilerEngine -from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - ScenarioParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - generated_scenario, - native_scenario_answer, - scenario_outcome, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - ScenarioParityTestCase( - description="generic syntax", - seed=20261008, - count=3000, - syntax=SqlLexicalSyntax(), - expected_minimum_extracted=150, - expected_minimum_native=80, - expected_minimum_python_errors=2000, - expected_minimum_native_errors=1200, - ), - ScenarioParityTestCase( - description="hash comments under the adapter's rules", - seed=20261009, - count=1000, - syntax=SqlLexicalSyntax(line_comment_prefixes=frozenset({"--", "#"})), - expected_minimum_extracted=50, - expected_minimum_native=25, - expected_minimum_python_errors=600, - expected_minimum_native_errors=400, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_scenarios_when_extracting_with_preview_then_python_output_matches( - test_case: ScenarioParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - sqls: list[str] = [generated_scenario(rng=rng) for _ in range(test_case.count)] - - python: list[object] = [ - scenario_outcome( - sql=sql, syntax=test_case.syntax, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch - ) - for sql in sqls - ] - answers: list[str] = [native_scenario_answer(sql=sql, syntax=test_case.syntax) for sql in sqls] - preview: list[object] = [ - scenario_outcome( - sql=sql, - syntax=test_case.syntax, - engine=CompilerEngine.NATIVE_PREVIEW, - monkeypatch=monkeypatch, - ) - for sql in sqls - ] - - assert ( - mismatches(inputs=[*sqls], expected=python, actual=preview), - sum(isinstance(item, CompileSqlScenarioCtes) for item in python) - >= test_case.expected_minimum_extracted, - answers.count("extracted") >= test_case.expected_minimum_native, - sum(isinstance(item, str) for item in python) >= test_case.expected_minimum_python_errors, - answers.count("error") >= test_case.expected_minimum_native_errors, - ) == ([], True, True, True, True), (test_case.description, Counter(answers)) - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_parameters.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_parameters.py deleted file mode 100644 index 871d0e1a15..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_parameters.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SQL test `@param` references expand identically under the preview engine.""" - -from __future__ import annotations - -import random - -import pytest - -from sqlbuild.compiler.frontier.types import CompilerEngine -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - ParameterParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - generated_parameter_sql, - parameter_outcome, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - ParameterParityTestCase( - description="seeded references, malformed and undeclared names, quotes, comments", - seed=20261008, - count=3000, - expected_minimum_expanded=800, - expected_minimum_python_errors=800, - expected_minimum_exact_errors=1500, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_test_bodies_when_expanding_parameters_then_python_output_matches( - test_case: ParameterParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - sqls: list[str] = [generated_parameter_sql(rng=rng) for _ in range(test_case.count)] - - python: list[object] = [ - parameter_outcome(sql=sql, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch) - for sql in sqls - ] - preview: list[object] = [ - parameter_outcome(sql=sql, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch) - for sql in sqls - ] - - assert ( - mismatches(inputs=[*sqls], expected=python, actual=preview), - sum(isinstance(item, tuple) for item in python) >= test_case.expected_minimum_expanded, - sum(isinstance(item, str) for item in python) >= test_case.expected_minimum_python_errors, - sum(isinstance(item, str) for item in preview) >= test_case.expected_minimum_exact_errors, - ) == ([], True, True, True), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_targets.py b/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_targets.py deleted file mode 100644 index a0840a9ac6..0000000000 --- a/tests/integration/src/sqlbuild/compiler/attachments/test_native_test_targets.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Model SQL test targets validate identically under the preview engine.""" - -from __future__ import annotations - -import random - -import pytest - -from sqlbuild.compiler.compile.models import CompileModelSqlTestCtes -from sqlbuild.compiler.frontier.types import CompilerEngine -from tests.integration.src.sqlbuild.compiler.attachments._test_types import ( - TargetParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.attachments.helpers import ( - generated_test_targets, - target_validation_outcome, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - - -@pytest.mark.parametrize( - "test_case", - [ - TargetParityTestCase( - description="seeded mocks, macro mocks, expected models and assertion targets", - seed=20261008, - count=2000, - expected_minimum_valid=150, - expected_minimum_python_errors=1000, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_test_targets_when_validating_with_preview_then_python_errors_match( - test_case: TargetParityTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - rng: random.Random = random.Random(test_case.seed) - targets: list[tuple[CompileModelSqlTestCtes, tuple[str, ...]]] = [ - generated_test_targets(rng=rng) for _ in range(test_case.count) - ] - - python: list[str | None] = [ - target_validation_outcome( - payload=payload, - assertion_targets=assertion_targets, - engine=CompilerEngine.PYTHON, - monkeypatch=monkeypatch, - ) - for payload, assertion_targets in targets - ] - preview: list[str | None] = [ - target_validation_outcome( - payload=payload, - assertion_targets=assertion_targets, - engine=CompilerEngine.NATIVE_PREVIEW, - monkeypatch=monkeypatch, - ) - for payload, assertion_targets in targets - ] - - assert ( - mismatches(inputs=[*targets], expected=[*python], actual=[*preview]), - python.count(None) >= test_case.expected_minimum_valid, - len(python) - python.count(None) >= test_case.expected_minimum_python_errors, - ) == ([], True, True), test_case.description - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py index 2267a116aa..678f8f8ce9 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py @@ -18,7 +18,6 @@ class MacroBridgeMemoTestCase: description: str files: dict[str, str] - expected_python_executions: int expected_native_executions: int diff --git a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py index cb5135b328..270ec0d091 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py +++ b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py @@ -1,4 +1,4 @@ -"""The native macro bridge renders compile inputs byte-identically to the Python engine.""" +"""The native macro bridge renders compile inputs as expected, running each call class once.""" from __future__ import annotations @@ -73,22 +73,22 @@ ], ids=lambda case: case.description, ) -def test_given_macro_heavy_project_when_rendering_with_each_engine_then_inputs_are_identical( +def test_given_macro_heavy_project_when_rendering_with_each_engine_then_inputs_are_expected( test_case: MacroBridgeParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: project_dir: Path = write_project(root=tmp_path / "project", files=test_case.files) - python_capture: str = comparable_capture( - render_compile_inputs(project_dir=project_dir, engine="python", monkeypatch=monkeypatch) - ) native_capture: str = comparable_capture( + render_compile_inputs(project_dir=project_dir, engine="native", monkeypatch=monkeypatch) + ) + preview_capture: str = comparable_capture( render_compile_inputs( project_dir=project_dir, engine="native-preview", monkeypatch=monkeypatch ) ) - assert native_capture == python_capture - assert all(map(python_capture.__contains__, test_case.expected_fragments)) + assert preview_capture == native_capture + assert all(map(native_capture.__contains__, test_case.expected_fragments)) @pytest.mark.parametrize( @@ -97,7 +97,6 @@ def test_given_macro_heavy_project_when_rendering_with_each_engine_then_inputs_a MacroBridgeMemoTestCase( description="one global call text in three models", files=MACRO_BRIDGE_PROJECT_FILES, - expected_python_executions=3, expected_native_executions=1, ), ], @@ -108,14 +107,14 @@ def test_given_repeated_macro_calls_when_rendering_natively_then_each_call_class ) -> None: project_dir: Path = write_project(root=tmp_path / "project", files=test_case.files) executions: dict[str, int] = {} - for engine in ("python", "native-preview"): + for engine in ("native", "native-preview"): log_path: Path = tmp_path / f"{engine}.log" monkeypatch.setenv(MACRO_CALL_LOG_ENV_VAR, str(log_path)) _ = render_compile_inputs(project_dir=project_dir, engine=engine, monkeypatch=monkeypatch) executions[engine] = len(log_path.read_text(encoding="utf-8").splitlines()) assert executions == { - "python": test_case.expected_python_executions, + "native": test_case.expected_native_executions, "native-preview": test_case.expected_native_executions, } @@ -168,20 +167,20 @@ def test_given_repeated_macro_calls_when_rendering_natively_then_each_call_class ], ids=lambda case: case.description, ) -def test_given_failing_render_when_rendering_natively_then_raises_the_python_error( +def test_given_failing_render_when_rendering_natively_then_raises_the_expected_error( test_case: MacroBridgeFailureTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: project_dir: Path = write_project(root=tmp_path / "project", files=test_case.files) - python_error: tuple[type[BaseException], str, type[object]] = render_error( - project_dir=project_dir, engine="python", monkeypatch=monkeypatch - ) native_error: tuple[type[BaseException], str, type[object]] = render_error( + project_dir=project_dir, engine="native", monkeypatch=monkeypatch + ) + preview_error: tuple[type[BaseException], str, type[object]] = render_error( project_dir=project_dir, engine="native-preview", monkeypatch=monkeypatch ) - assert native_error == python_error - assert test_case.expected_message_fragment in python_error[1] + assert preview_error == native_error + assert test_case.expected_message_fragment in native_error[1] @pytest.mark.parametrize( @@ -235,7 +234,7 @@ def test_given_random_macro_sql_when_expanding_through_bridge_then_matches_pytho description="seeded projects with raising, mistyped, unparsable and unreachable macros", seed=1043, projects=150, - engines=("python", "native", "native-preview"), + engines=("native", "native-preview"), expected_minimum_failures=100, expected_minimum_successes=10, expected_minimum_errors_after_macro_runs=80, @@ -245,7 +244,7 @@ def test_given_random_macro_sql_when_expanding_through_bridge_then_matches_pytho ], ids=lambda case: case.description, ) -def test_given_failing_macro_projects_when_rendering_then_every_engine_raises_python_error_once( +def test_given_failing_macro_projects_when_rendering_then_every_engine_raises_one_error( test_case: FailingMacroProjectParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, diff --git a/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py b/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py index 3f8546f893..d646212488 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py @@ -19,16 +19,6 @@ class GeneratedContractParityTestCase: expected_codes: frozenset[str] -@dataclass(frozen=True) -class GeneratedPromotionParityTestCase: - """Generated lifecycle configs and promotion settings compared with Python.""" - - description: str - seed: int - variants: int - expected_minimum_conflicts: int - - @dataclass(frozen=True) class DeferredContractTestCase: """A contract input native validation hands back to Python, and the deferral it records.""" diff --git a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py index 19b79f5805..418254a266 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py @@ -14,7 +14,6 @@ import pytest import sqlbuild._native as native_module -from sqlbuild.adapter.contract.types import TablePromotionMode from sqlbuild.adapter.type_system._helpers.type_normalization import normalize_type from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile.models import ( @@ -25,7 +24,10 @@ DynamicColumnFamilyProof, InferredColumn, ) -from sqlbuild.compiler.contracts.main.promotion_conflicts import promotion_conflict_diagnostics +from sqlbuild.compiler.contracts._helpers.evaluation import ( + python_model_contract_diagnostics, + requires_contract_evaluation, +) from sqlbuild.compiler.contracts.main.validate import evaluate_model_contracts from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR @@ -163,7 +165,6 @@ None, ) _PROMOTION_MODES: tuple[str | None, ...] = (None, "immediate", "staged", "IMMEDIATE", "") -_SETTINGS_FILES: tuple[str, ...] = ("sqlbuild_project.toml", "sqlbuild_local.toml") type ContractView = tuple[CompilerDiagnostic, ...] type NativeContractRequest = tuple[str, bool, list[Any]] @@ -290,13 +291,13 @@ def contract_views( dialect: str | None, monkeypatch: pytest.MonkeyPatch, ) -> tuple[ContractView, ContractView]: - """Python's and the native engine's contract diagnostics for the same project.""" + """The Python contract diagnostics native defers to, and native's, for the same project.""" return ( _on_engine( - engine="python", + engine="native", monkeypatch=monkeypatch, - run=lambda: evaluate_model_contracts(project=project, dialect=dialect).diagnostics, + run=lambda: _python_contract_diagnostics(project=project, dialect=dialect), ), _on_engine( engine="native-preview", @@ -306,30 +307,19 @@ def contract_views( ) -def promotion_views( - *, - project: CompiledProject, - adapter_default: TablePromotionMode, - settings_file: str, - monkeypatch: pytest.MonkeyPatch, -) -> tuple[ContractView, ContractView]: - """Python's and the native engine's K011 promotion conflicts for the same project.""" - - def run() -> ContractView: - return promotion_conflict_diagnostics( - project=project, adapter_default=adapter_default, settings_file=settings_file - ) - - return ( - _on_engine(engine="python", monkeypatch=monkeypatch, run=run), - _on_engine(engine="native-preview", monkeypatch=monkeypatch, run=run), +def _python_contract_diagnostics( + *, project: CompiledProject, dialect: str | None +) -> tuple[CompilerDiagnostic, ...]: + mode: ColumnContractMode = project.settings.column_contract_mode + evaluated: list[CompiledModel] = list( + filter(lambda model: requires_contract_evaluation(model=model, mode=mode), project.models) ) - - -def promotion_settings(*, rng: random.Random) -> tuple[TablePromotionMode, str]: - """A generated adapter default promotion mode and settings file.""" - - return rng.choice(tuple(TablePromotionMode)), rng.choice(_SETTINGS_FILES) + diagnostics: list[CompilerDiagnostic] = [] + for model in evaluated: + diagnostics.extend( + python_model_contract_diagnostics(model=model, mode=mode, dialect=dialect) + ) + return tuple(diagnostics) def _on_engine( @@ -430,24 +420,6 @@ def _compares_typed_column(*, model: Sequence[Any], implicit: bool) -> bool: ) -def record_native_promotion_calls(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count native promotion conflict calls and the conflicts they return.""" - - statuses: Counter[str] = Counter() - conflicts: Callable[..., list[tuple[int, str, str, str]]] = ( - native_module.native_promotion_conflicts - ) - - def counted(*arguments: Any) -> list[tuple[int, str, str, str]]: - found: list[tuple[int, str, str, str]] = conflicts(*arguments) - statuses["calls"] += 1 - statuses["conflicts"] += len(found) - return found - - monkeypatch.setattr(native_module, "native_promotion_conflicts", counted) - return statuses - - def deferral_records(directory: Path) -> list[dict[str, str]]: """Every deferral record written under `directory`, in file order.""" diff --git a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py index 27d295ce33..65b4aa8400 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py @@ -3,7 +3,6 @@ from __future__ import annotations import random -from collections import Counter from pathlib import Path import pytest @@ -13,7 +12,6 @@ from tests.integration.src.sqlbuild.compiler.contracts._test_types import ( DeferredContractTestCase, GeneratedContractParityTestCase, - GeneratedPromotionParityTestCase, ) from tests.integration.src.sqlbuild.compiler.contracts.helpers import ( NativeContractRecord, @@ -21,10 +19,7 @@ contract_views, deferral_records, perturbed_project, - promotion_settings, - promotion_views, record_native_outcomes, - record_native_promotion_calls, with_declared_type, ) from tests.integration.src.sqlbuild.compiler.helpers import mismatches @@ -74,47 +69,6 @@ def test_given_generated_contracts_when_validating_natively_then_diagnostics_mat assert set(record.codes) >= test_case.expected_codes -@pytest.mark.parametrize( - "test_case", - [ - GeneratedPromotionParityTestCase( - description="lifecycle configs under explicit and adapter-default promotion modes", - seed=20261010, - variants=400, - expected_minimum_conflicts=30, - ) - ], - ids=lambda case: case.description, -) -def test_given_generated_lifecycles_when_finding_promotion_conflicts_natively_then_match_python( - test_case: GeneratedPromotionParityTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - rng: random.Random = random.Random(test_case.seed) - base: CompiledProject = compiled_contract_project(project_dir=tmp_path / "project") - calls: Counter[str] = record_native_promotion_calls(monkeypatch=monkeypatch) - labels: list[object] = [] - python_views: list[object] = [] - native_views: list[object] = [] - for variant in range(test_case.variants): - project: CompiledProject = perturbed_project(project=base, rng=rng) - adapter_default, settings_file = promotion_settings(rng=rng) - python, native = promotion_views( - project=project, - adapter_default=adapter_default, - settings_file=settings_file, - monkeypatch=monkeypatch, - ) - labels.append(variant) - python_views.append(python) - native_views.append(native) - - assert mismatches(inputs=labels, expected=python_views, actual=native_views) == [] - assert calls["calls"] == test_case.variants - assert calls["conflicts"] >= test_case.expected_minimum_conflicts - - @pytest.mark.parametrize( "test_case", [ diff --git a/tests/integration/src/sqlbuild/compiler/discovery/_test_types.py b/tests/integration/src/sqlbuild/compiler/discovery/_test_types.py index ce3c94f921..c7dbc3e5a1 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/_test_types.py @@ -47,15 +47,6 @@ class LargeDocumentOracleTestCase: expected_maximum_seconds: float = 2.0 -@dataclass(frozen=True) -class EngineSwitchParityTestCase: - """A project discovered through `discover_project_inputs` under each engine.""" - - description: str - files: tuple[tuple[str, bytes], ...] - expected_identical: bool = True - - @dataclass(frozen=True) class NativeRuntimeTestCase: """A project and runtime under which native model discovery runs or fails clearly.""" diff --git a/tests/integration/src/sqlbuild/compiler/discovery/helpers.py b/tests/integration/src/sqlbuild/compiler/discovery/helpers.py index 33223ae925..64308bde13 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/helpers.py @@ -1235,8 +1235,8 @@ def generated_declaration_outcomes( write_project( project_dir=project_dir, files=((test_case.relative_path, contents.encode("utf-8")),) ) - python: tuple[object, ...] = declaration_files_outcome( - project_dir=project_dir, kind=test_case.kind, engine="python", monkeypatch=monkeypatch + shipped: tuple[object, ...] = declaration_files_outcome( + project_dir=project_dir, kind=test_case.kind, engine="native", monkeypatch=monkeypatch ) native: tuple[object, ...] = declaration_files_outcome( project_dir=project_dir, @@ -1249,7 +1249,7 @@ def generated_declaration_outcomes( file_path=project_dir / test_case.relative_path, kind=test_case.kind ) ] += 1 - agreements.append(native == python) + agreements.append(native == shipped) return tuple(compress(range(test_case.case_count), map(not_, agreements))), tags diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_deep_header_values.py b/tests/integration/src/sqlbuild/compiler/discovery/test_deep_header_values.py index 669e057847..f81951c945 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_deep_header_values.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/test_deep_header_values.py @@ -35,7 +35,7 @@ description="every container kind at the limit beneath 300 extra frames", header=_AT_LIMIT_HEADER, stack_frames=300, - expected_engines=("python", "native", "native-preview"), + expected_engines=("native", "native-preview"), expected_max_depth=769, ) ], diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_discovery_failure_text.py b/tests/integration/src/sqlbuild/compiler/discovery/test_discovery_failure_text.py index 7cff580a22..808d10524b 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_discovery_failure_text.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/test_discovery_failure_text.py @@ -18,7 +18,7 @@ write_project, ) -_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") +_ENGINES: tuple[str, ...] = ("native", "native-preview") _MODEL: tuple[str, bytes] = ("models/orders.sql", b"MODEL ();\nSELECT 1 AS order_id") _HOOK: tuple[str, bytes] = ("hooks/sql/refresh.sql", b"HOOK (descri\x00ption 'x');\nSELECT 1") _CONSTANT: tuple[str, bytes] = ("constants/limits.sql", b"CONSTANT (name cap, valu\x00e 1);") @@ -101,12 +101,12 @@ def test_given_failing_project_when_discovering_then_every_engine_shows_authored ], ids=lambda case: case.description, ) -def test_given_nul_header_keys_when_discovering_tolerantly_then_faults_match_python( +def test_given_nul_header_keys_when_discovering_tolerantly_then_faults_match( test_case: TolerantFailureTextTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: write_project(project_dir=tmp_path, files=test_case.files) - python: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( - tolerant_declaration_outcome(project_dir=tmp_path, engine="python", monkeypatch=monkeypatch) + shipped: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( + tolerant_declaration_outcome(project_dir=tmp_path, engine="native", monkeypatch=monkeypatch) ) native: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( @@ -115,7 +115,7 @@ def test_given_nul_header_keys_when_discovering_tolerantly_then_faults_match_pyt ) ) - assert native == python + assert native == shipped assert ( tuple(sorted(fault.message.rsplit("keys: ", 1)[1] for fault in (*native[1], *native[2]))) == test_case.expected_fault_keys diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_discovery.py b/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_discovery.py deleted file mode 100644 index 27e9443978..0000000000 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_discovery.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Engine parity for the declaration layout native discovery walks and validates.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from tests.integration.src.sqlbuild.compiler.discovery._test_types import ( - EngineSwitchParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.discovery.helpers import ( - stage_outcome, - write_project, -) - -_AUDIT: bytes = b'AUDIT ();\n\nSELECT *\nFROM __ref("@model")\nWHERE NOT (@expression)\n' -_SCHEMA: bytes = ( - b'SCHEMA (\n name order_shape,\n description "Order shape",\n' - b" columns (\n id (type INTEGER),\n ),\n);\n" -) -_HOOK: bytes = b'HOOK (\n description "Record a refresh"\n);\n\nSELECT 1 AS refreshed\n' -_MACRO: bytes = b"def cents(value):\n return f'{value} * 100'\n" -_MODEL: bytes = b'MODEL (description "Orders");\nSELECT 1 AS order_id' - - -@pytest.mark.parametrize( - "test_case", - [ - EngineSwitchParityTestCase( - description="global, inherited, local and grouped declarations of every kind", - files=( - ("models/marts/orders.sql", _MODEL), - ("macros/money.py", _MACRO), - ("macros/__init__.py", b""), - ("enums/status.sql", b"ENUM (name order_status, members [PLACED]);\n"), - ("models/marts/_enums/tier.sql", b"ENUM (name tier, members [GOLD]);\n"), - ("models/marts/_sqlbuild/constants/limits.sql", b"CONSTANT (name cap, value 3);\n"), - ("models/marts/_sqlbuild/_audits/generic/is_true.sql", _AUDIT), - ("models/marts/_sqlbuild/_schemas/order_shape.sql", _SCHEMA), - ("models/marts/_sqlbuild/hooks/sql/record_refresh.sql", _HOOK), - ("audits/generic/is_true.sql", _AUDIT), - ("schemas/order_shape.sql", _SCHEMA), - ("hooks/sql/_private.sql", _HOOK), - ("functions/sql/_macros/helpers.py", _MACRO), - ("seeds/channels.csv", b"id\n1\n"), - ), - ), - EngineSwitchParityTestCase( - description="a nested declaration root fails with Python's error", - files=(("models/marts/_enums/macros/money.py", _MACRO),), - ), - EngineSwitchParityTestCase( - description="an unsupported audit role entry fails with Python's error", - files=(("models/marts/orders.sql", _MODEL), ("audits/is_true.sql", _AUDIT)), - ), - EngineSwitchParityTestCase( - description="an unreadable declaration file fails with Python's read error", - files=(("enums/status.sql", b"\xff"),), - ), - ], - ids=lambda case: case.description, -) -def test_given_declaration_layout_when_discovering_through_the_engine_switch_then_inputs_match( - test_case: EngineSwitchParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - write_project(project_dir=tmp_path, files=test_case.files) - python: object = stage_outcome(project_dir=tmp_path, engine="python", monkeypatch=monkeypatch) - - native: object = stage_outcome(project_dir=tmp_path, engine="native", monkeypatch=monkeypatch) - - assert (native == python) is test_case.expected_identical, test_case.description diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_files.py b/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_files.py index cbea4e7bcd..09855850ca 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_files.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/test_native_declaration_files.py @@ -269,15 +269,15 @@ def test_given_declaration_files_when_discovering_through_the_engine_switch_then test_case: DeclarationFilesParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: write_project(project_dir=tmp_path, files=test_case.files) - python: tuple[object, ...] = stage_outcome( - project_dir=tmp_path, engine="python", monkeypatch=monkeypatch + shipped: tuple[object, ...] = stage_outcome( + project_dir=tmp_path, engine="native", monkeypatch=monkeypatch ) native: tuple[object, ...] = stage_outcome( project_dir=tmp_path, engine=_PREVIEW, monkeypatch=monkeypatch ) - assert native == python + assert native == shipped assert native[1] == test_case.expected_failure_type assert test_case.expected_message_fragment in str(native[2]) @@ -334,15 +334,15 @@ def test_given_deeply_nested_declaration_header_when_discovering_then_engines_re nested: str = "[" * test_case.depth + "1" + "]" * test_case.depth contents: str = test_case.prefix + nested + test_case.suffix write_project(project_dir=tmp_path, files=((test_case.relative_path, contents.encode()),)) - python: tuple[object, ...] = declaration_files_outcome( - project_dir=tmp_path, kind=test_case.kind, engine="python", monkeypatch=monkeypatch + shipped: tuple[object, ...] = declaration_files_outcome( + project_dir=tmp_path, kind=test_case.kind, engine="native", monkeypatch=monkeypatch ) native: tuple[object, ...] = declaration_files_outcome( project_dir=tmp_path, kind=test_case.kind, engine=_PREVIEW, monkeypatch=monkeypatch ) - assert native == python + assert native == shipped assert native[0] == test_case.expected_failure_type assert str(native[1]).endswith(test_case.expected_message_suffix), native[1] assert native[2] == test_case.expected_help @@ -376,15 +376,15 @@ def test_given_broken_declaration_files_when_discovering_tolerantly_then_faults_ test_case: TolerantDeclarationFilesTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: write_project(project_dir=tmp_path, files=test_case.files) - python: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( - tolerant_declaration_outcome(project_dir=tmp_path, engine="python", monkeypatch=monkeypatch) + shipped: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( + tolerant_declaration_outcome(project_dir=tmp_path, engine="native", monkeypatch=monkeypatch) ) native: tuple[object, tuple[DiscoveryFileFault, ...], tuple[DiscoveryFileFault, ...]] = ( tolerant_declaration_outcome(project_dir=tmp_path, engine=_PREVIEW, monkeypatch=monkeypatch) ) - assert native == python + assert native == shipped assert len(native[1]) == test_case.expected_resource_faults assert len(native[2]) == test_case.expected_declaration_faults @@ -402,11 +402,6 @@ def test_given_broken_declaration_files_when_discovering_tolerantly_then_faults_ engine="native", expected_session=True, ), - NativeSessionTestCase( - description="the Python engine keeps it too, since declaration files are native-only", - engine="python", - expected_session=True, - ), ], ids=lambda case: case.description, ) diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_native_model_discovery.py b/tests/integration/src/sqlbuild/compiler/discovery/test_native_model_discovery.py index 400211148f..f8793afb29 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_native_model_discovery.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/test_native_model_discovery.py @@ -15,12 +15,8 @@ discover_native_model_files, ) from sqlbuild.compiler.discovery.classes.directory_snapshot import DirectorySnapshot -from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile -from sqlbuild.compiler.frontier._helpers.stage_capture import render_stage_capture -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from tests.integration.src.sqlbuild.compiler.discovery._test_types import ( - EngineSwitchParityTestCase, NativeRuntimeTestCase, SharedSnapshotTestCase, UnsupportedPythonCommandTestCase, @@ -33,35 +29,6 @@ _VALID_MODEL: bytes = b"MODEL (materialized table);\nSELECT order_id, total AS amount FROM orders" -@pytest.mark.parametrize( - "test_case", - [ - EngineSwitchParityTestCase( - description="models, model-local declarations and a macro", - files=( - ("models/orders.sql", _VALID_MODEL), - ( - "models/marts/customers.sql", - b"MODEL (enums (_tier [GOLD, SILVER]));\nSELECT 1 AS id", - ), - ("macros/money.py", b"def cents(value):\n return f'{value} * 100'\n"), - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_project_when_discovering_through_the_engine_switch_then_inputs_match( - test_case: EngineSwitchParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - write_project(project_dir=tmp_path, files=test_case.files) - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, "python") - python: str = render_stage_capture(discover_project_inputs(project_dir=tmp_path)) - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, "native") - native: str = render_stage_capture(discover_project_inputs(project_dir=tmp_path)) - - assert (native == python) is test_case.expected_identical - - @pytest.mark.parametrize( "test_case", [ diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_native_sql_test_discovery.py b/tests/integration/src/sqlbuild/compiler/discovery/test_native_sql_test_discovery.py deleted file mode 100644 index 957191f4c1..0000000000 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_native_sql_test_discovery.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Engine parity for SQL unit-test and scenario files discovered natively.""" - -from __future__ import annotations - -from pathlib import Path - -import pytest - -from tests.integration.src.sqlbuild.compiler.discovery._test_types import ( - EngineSwitchParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.discovery.helpers import ( - stage_outcome, - write_project, -) - -_TEST: bytes = b'TEST (name "keeps_orders");\n\nSELECT 1\n' -_SCENARIO: bytes = b'SCENARIO (description "Orders world");\n\nSELECT 1\n' - - -@pytest.mark.parametrize( - "test_case", - [ - EngineSwitchParityTestCase( - description="test and scenario files in nested, macro and scoped folders", - files=( - ("models/orders.sql", b'MODEL (description "Orders");\nSELECT 1 AS order_id'), - ("tests/unit/orders.sql", _TEST), - ("tests/unit/marts/b.sql", _TEST), - ("tests/unit/macros/money.sql", _TEST), - ("tests/unit/marts/_macros/helpers.py", b"def cents(value):\n return value\n"), - ("tests/unit/marts/_sqlbuild/enums/status.sql", b"ENUM (name s, members [A]);\n"), - ("tests/scenarios/north/orders.sql", _SCENARIO), - ("tests/scenarios/south.sql", _SCENARIO), - ), - ), - EngineSwitchParityTestCase( - description="the first failing test file in path order wins", - files=( - ("tests/unit/a.sql", _TEST), - ("tests/unit/b.sql", b"\xff"), - ("tests/unit/c.sql", b"TEST (nme 1);\nSELECT 1"), - ), - ), - EngineSwitchParityTestCase( - description="a semantic failure in an earlier block beats a later syntax failure", - files=( - ("tests/unit/a.sql", b"TEST (mode bogus);\nSELECT 1\nTEST (name ();\nSELECT 2"), - ), - ), - EngineSwitchParityTestCase( - description="an unreadable scenario fails with Python's read error", - files=(("tests/scenarios/a.sql", b"SCENARIO ();\n\xfe"),), - ), - ], - ids=lambda case: case.description, -) -def test_given_test_files_when_discovering_through_the_engine_switch_then_inputs_match( - test_case: EngineSwitchParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - write_project(project_dir=tmp_path, files=test_case.files) - python: object = stage_outcome(project_dir=tmp_path, engine="python", monkeypatch=monkeypatch) - - native: object = stage_outcome(project_dir=tmp_path, engine="native", monkeypatch=monkeypatch) - - assert (native == python) is test_case.expected_identical, test_case.description diff --git a/tests/integration/src/sqlbuild/compiler/discovery/test_native_yaml_file_discovery.py b/tests/integration/src/sqlbuild/compiler/discovery/test_native_yaml_file_discovery.py index f5fe19f68a..aa2c21dd88 100644 --- a/tests/integration/src/sqlbuild/compiler/discovery/test_native_yaml_file_discovery.py +++ b/tests/integration/src/sqlbuild/compiler/discovery/test_native_yaml_file_discovery.py @@ -7,12 +7,10 @@ import pytest from tests.integration.src.sqlbuild.compiler.discovery._test_types import ( - EngineSwitchParityTestCase, NativeYamlLoadTestCase, ) from tests.integration.src.sqlbuild.compiler.discovery.helpers import ( native_yaml_tags_and_values, - stage_outcome, write_project, ) @@ -29,51 +27,6 @@ ) -@pytest.mark.parametrize( - "test_case", - [ - EngineSwitchParityTestCase( - description="sources, model schema and seed declarations", - files=( - ("sources/orders.yml", _SOURCES), - ("sources/more.yaml", _SOURCES.replace(b"raw_orders", b"raw_customers")), - ("sources/notes.txt", b"ignored"), - ("models/orders.sql", b'MODEL (description "Orders");\nSELECT 1 AS order_id'), - ("models/marts/schema.yml", b"models: []\n"), - ("models/marts/_sqlbuild/schemas/schema.yml", b"models: []\n"), - ("seeds/channels.yml", _SEED), - ("seeds/channels.csv", b"id\n1\n"), - ), - ), - EngineSwitchParityTestCase( - description="a Python-only tag and invalid YAML load in Python with its error", - files=( - ("sources/a.yml", b"sources: []\nextra: !!set {a: null}\n"), - ("sources/b.yml", b"sources: [\n"), - ), - ), - EngineSwitchParityTestCase( - description="an unreadable source file fails with Python's read error", - files=(("sources/a.yml", b"sources: []\n# \xff\n"),), - ), - EngineSwitchParityTestCase( - description="a directory named like a source file fails as Python reads it", - files=(("sources/folder.yml/inner.txt", b""),), - ), - ], - ids=lambda case: case.description, -) -def test_given_yaml_files_when_discovering_through_the_engine_switch_then_inputs_match( - test_case: EngineSwitchParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - write_project(project_dir=tmp_path, files=test_case.files) - python: object = stage_outcome(project_dir=tmp_path, engine="python", monkeypatch=monkeypatch) - - native: object = stage_outcome(project_dir=tmp_path, engine="native", monkeypatch=monkeypatch) - - assert (native == python) is test_case.expected_identical, test_case.description - - @pytest.mark.parametrize( "test_case", [ diff --git a/tests/integration/src/sqlbuild/compiler/model_config/test_native_model_config.py b/tests/integration/src/sqlbuild/compiler/model_config/test_native_model_config.py index fdee70229f..7e88a5701c 100644 --- a/tests/integration/src/sqlbuild/compiler/model_config/test_native_model_config.py +++ b/tests/integration/src/sqlbuild/compiler/model_config/test_native_model_config.py @@ -49,14 +49,6 @@ def test_given_generated_header_metadata_when_parsing_then_native_matches_yaml_s @pytest.mark.parametrize( "test_case", [ - ModelConfigTierTestCase( - description="python oracle", - engine="python", - expected_native_calls={ - "parse_model_header_metadata": 2, - "expand_config_templates": 1, - }, - ), ModelConfigTierTestCase( description="shipped native stages", engine="native", @@ -82,13 +74,13 @@ def test_given_engine_tier_when_building_model_inputs_then_every_engine_runs_nat calls, config = model_config_engine_outcome( project_dir=tmp_path / test_case.engine, engine=test_case.engine, monkeypatch=monkeypatch ) - _, python_config = model_config_engine_outcome( - project_dir=tmp_path / "python", engine="python", monkeypatch=monkeypatch + _, shipped_config = model_config_engine_outcome( + project_dir=tmp_path / "shipped", engine="native", monkeypatch=monkeypatch ) - assert (calls, config.replace(test_case.engine, "python")) == ( + assert (calls, config.replace(test_case.engine, "shipped")) == ( test_case.expected_native_calls, - python_config, + shipped_config, ) diff --git a/tests/integration/src/sqlbuild/compiler/model_loop/__init__.py b/tests/integration/src/sqlbuild/compiler/model_loop/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/integration/src/sqlbuild/compiler/model_loop/_test_types.py b/tests/integration/src/sqlbuild/compiler/model_loop/_test_types.py deleted file mode 100644 index 9d48db4f9f..0000000000 --- a/tests/integration/src/sqlbuild/compiler/model_loop/_test_types.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import annotations diff --git a/tests/integration/src/sqlbuild/compiler/model_loop/helpers.py b/tests/integration/src/sqlbuild/compiler/model_loop/helpers.py deleted file mode 100644 index bc508ca764..0000000000 --- a/tests/integration/src/sqlbuild/compiler/model_loop/helpers.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Generated SQL and declaration contexts for the native model loop parity tests.""" - -from __future__ import annotations - -import random -from pathlib import Path - -from sqlbuild.compiler.compile.models import ( - DeclarationResolutionContext, -) -from sqlbuild.compiler.discovery.models import ConstantDeclaration, EnumDeclaration, EnumMember -from sqlbuild.compiler.scopes.models import ( - DeclarationIdentity, - DeclarationRecord, - OwnershipRoot, - ResourceIdentity, - VisibilityRecord, -) -from sqlbuild.compiler.scopes.types import ( - DeclarationKind, - OwnershipRootKind, - ResourceKind, - ScopeKind, - VisibilityReason, -) -from sqlbuild.sql_values.main.normalize import normalize_sql_value - -_CONSUMER: ResourceIdentity = ResourceIdentity(ResourceKind.MODEL, "orders") -_FILE_PATH: Path = Path("models/orders.sql") -_ENUMS: dict[str, EnumDeclaration] = { - "order_status": EnumDeclaration( - "order_status", - (EnumMember("PLACED", "placed"), EnumMember("SHIPPED", 2), EnumMember("NOTED", "it's")), - "VARCHAR", - Path("enums/status.sql"), - ), -} -_CONSTANTS: dict[str, ConstantDeclaration] = { - "sales_cap": ConstantDeclaration( - "sales_cap", normalize_sql_value(raw_value=3, context="test"), Path("constants/cap.sql") - ), - "label": ConstantDeclaration( - "label", normalize_sql_value(raw_value="o'k \u00e9", context="test"), Path("c/label.sql") - ), - "regions": ConstantDeclaration( - "regions", - normalize_sql_value(raw_value=["north", "south"], context="test"), - Path("constants/regions.sql"), - ), - "quoted_note": ConstantDeclaration( - "quoted_note", - normalize_sql_value(raw_value="'" * 600_000, context="test"), - Path("constants/note.sql"), - ), -} -DECLARATIONS: DeclarationResolutionContext = DeclarationResolutionContext( - enums=_ENUMS, - constants=_CONSTANTS, - enum_visibility={ - "order_status": ( - VisibilityRecord( - _CONSUMER, - DeclarationIdentity(DeclarationKind.ENUM, "order_status"), - VisibilityReason.GLOBAL, - ), - ) - }, - constant_visibility={ - name: ( - VisibilityRecord( - _CONSUMER, - DeclarationIdentity(DeclarationKind.CONSTANT, name), - VisibilityReason.GLOBAL, - ), - ) - for name in _CONSTANTS - }, - inaccessible_enums={ - "staged_status": DeclarationRecord( - identity=DeclarationIdentity(DeclarationKind.ENUM, "staged_status"), - path="models/staging/_enums/status.sql", - line=1, - column=1, - scope=ScopeKind.LOCAL, - ownership_root=OwnershipRoot("models", OwnershipRootKind.RESOURCE, ResourceKind.MODEL), - owning_path="models/staging", - ) - }, - inaccessible_constants={ - "staged_cap": DeclarationRecord( - identity=DeclarationIdentity(DeclarationKind.CONSTANT, "staged_cap"), - path="models/staging/_constants/cap.sql", - line=1, - column=1, - scope=ScopeKind.INHERITED, - ownership_root=OwnershipRoot("models", OwnershipRootKind.RESOURCE, ResourceKind.MODEL), - owning_path="models/staging", - ) - }, - consumer=_CONSUMER, -) -_WHITESPACE: tuple[str, ...] = ("", " ", "\t", "\n ", "\x1c", "\u00a0", "\u2003") -_REFERENCES: tuple[str, ...] = ( - '@enum({s}"order_status"{s}){s}.{s}PLACED', - "@enum('order_status').SHIPPED", - '@enum("order_status").NOTED', - '@const("sales_cap")', - "@const({s}'label'{s})", - '@const("regions")', -) -_INVALID_REFERENCES: tuple[str, ...] = ( - '@enum("order_status").MISSING', - '@const("unknown_cap")', - '@const("_private_cap")', - '@enum("_private_status").PLACED', - '@enum("staged_status").PLACED', - '@const("staged_cap")', - '@const("quoted_note")', - '@enum("order_status")', - "@const(sales_cap)", - "@enum", - "@const\u00a0(", -) -_TEXT: tuple[str, ...] = ( - "SELECT ", - " FROM orders", - ", ", - "caf\u00e9 ", - "\U0001f600 ", - "@constant ", - "@enumerate ", - "price$1 ", - '$$ @const("sales_cap") $$', - '$tag$ @enum("x").Y $tag$', - "'@const(\"sales_cap\")'", - "'it''s'", - '"quoted @const(\\"label\\")"', - "`tick`", - '-- @const("sales_cap")\n', - '/* @enum("order_status").PLACED */', - "@\u00e9 ", -) -_RARE: tuple[str, ...] = ( - "'unterminated ", - "/* open ", - "`open ", - "$q$ open ", - "@const\u00e9 ", - *_INVALID_REFERENCES, - *_INVALID_REFERENCES, -) - - -def generated_reference_sql(*, rng: random.Random) -> str: - """Return SQL mixing references, quoted text, comments and Unicode in random order.""" - - return _reference_sql(rng=rng, rare_parts=int(rng.random() < 0.15)) - - -def _reference_sql(*, rng: random.Random, rare_parts: int) -> str: - parts: list[str] = [ - rng.choices((_reference_part, _text_part), weights=(4, 6))[0](rng) - for _ in range(rng.randint(1, 12)) - ] - parts.extend(rng.choices(_RARE, k=rare_parts)) - rng.shuffle(parts) - return "".join(parts) - - -def _reference_part(rng: random.Random) -> str: - return rng.choice(_REFERENCES).format(s=rng.choice(_WHITESPACE)) - - -def _text_part(rng: random.Random) -> str: - return rng.choice(_TEXT) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py index 05f2af995f..e5d0bc806a 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py @@ -23,7 +23,7 @@ use_wave_analysis, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("python_compiler_engine") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py index d3ad46ddad..a3ed09e470 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py @@ -39,7 +39,7 @@ use_wave_analysis, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("python_compiler_engine") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") _PROJECT: RandomDagProject = RandomDagProject(seed=97, model_count=40, errors=True) _SHARED_ANALYSIS_SEED: int = 34 diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py index 997772606c..0f9fde469e 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py @@ -14,7 +14,7 @@ ExpressionMemoCase, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("python_compiler_engine") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py index 4d7cc12940..608743d829 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py @@ -15,7 +15,7 @@ write_shared_binding_project, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("python_compiler_engine") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py index c8b4ded2df..3f865b95da 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py @@ -8,6 +8,7 @@ from collections.abc import Callable from operator import methodcaller from pathlib import Path +from types import ModuleType from typing import Any import pytest @@ -40,6 +41,9 @@ ) _ASSEMBLY_RECORD_PREFIX: str = "project_assembly:" _OPT_OUT_VALIDATORS: tuple[str, ...] = ("validate_sql_syntax", "validate_hook_sql_syntax") +type NativeDeferrals = tuple[tuple[ModuleType, str], ...] +DEFERRED_PROJECT_FACTS: NativeDeferrals = ((project_module, "project_facts_by_engine"),) +DEFERRED_SYNTAX_CHECK: NativeDeferrals = ((sql_analysis_opt_outs, "check_native_sql_syntax"),) _ADAPTER_CONTEXT: CompileAdapterContext = CompileAdapterContext( value_renderer=DuckDbAdapter(), collection_rendering=CollectionRendering.VALUE_LIST, @@ -168,22 +172,32 @@ def project_inputs(*, project_dir: Path, files: dict[str, str]) -> CompileProjec def assemble_with( - *, inputs: CompileProjectInputs, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch + *, + inputs: CompileProjectInputs, + engine: CompilerEngine, + monkeypatch: pytest.MonkeyPatch, + deferred: NativeDeferrals = (), ) -> CompiledProject: - """Assemble `inputs` with `engine`.""" + """Assemble `inputs` with `engine`, making each native entry in `deferred` defer to Python.""" monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - return assemble_compiled_project(inputs=inputs) + with monkeypatch.context() as patch: + _ = [patch.setattr(module, name, _deferral) for module, name in deferred] + return assemble_compiled_project(inputs=inputs) def recorded_assembly( - *, inputs: CompileProjectInputs, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch + *, + inputs: CompileProjectInputs, + engine: CompilerEngine, + monkeypatch: pytest.MonkeyPatch, + deferred: NativeDeferrals = (), ) -> tuple[object, tuple[tuple[str, ...], bool]]: """Assemble `inputs` with `engine`; return its assembly view and the input reads recorded.""" with COMPILE_INPUT_READS.recording() as reads: project: CompiledProject = assemble_with( - inputs=inputs, engine=engine, monkeypatch=monkeypatch + inputs=inputs, engine=engine, monkeypatch=monkeypatch, deferred=deferred ) return assembly_view(project), (reads.environment_names, reads.read_run_id) @@ -207,12 +221,16 @@ def rejected_opt_outs( files: dict[str, str], engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch, + deferred: NativeDeferrals = (), ) -> tuple[dict[str, bool], int]: - """Attach `files` with `engine`; return each opt-out's rejection and Python validations.""" + """Attach `files` with `engine`; return each opt-out's rejection and Python validations. + + Each native entry in `deferred` defers, so Python answers its work.""" calls: Counter[str] = Counter() monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) with monkeypatch.context() as patch: + _ = [patch.setattr(module, name, _deferral) for module, name in deferred] _ = [ patch.setattr( sql_analysis_opt_outs, @@ -270,3 +288,7 @@ def counted(*args: Any, **kwargs: Any) -> Any: return function(*args, **kwargs) return counted + + +def _deferral(**_kwargs: object) -> None: + return None diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index e88e498591..514932ed6b 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -23,6 +23,8 @@ WindowsEnvironmentTestCase, ) from tests.integration.src.sqlbuild.compiler.project_assembly.helpers import ( + DEFERRED_PROJECT_FACTS, + DEFERRED_SYNTAX_CHECK, assemble_with, assembly_deferrals, assembly_view, @@ -83,7 +85,12 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( for index in range(test_case.count) ] python: list[tuple[object, tuple[tuple[str, ...], bool]]] = [ - recorded_assembly(inputs=item, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch) + recorded_assembly( + inputs=item, + engine=CompilerEngine.NATIVE, + monkeypatch=monkeypatch, + deferred=DEFERRED_PROJECT_FACTS, + ) for item in inputs ] python_calls: int = calls.total() @@ -174,7 +181,12 @@ def test_given_projects_python_rejects_when_assembling_natively_then_defers_with inputs: CompileProjectInputs = project_inputs(project_dir=tmp_path, files=test_case.files) with pytest.raises(CompileInputError) as python_error: - _ = assemble_with(inputs=inputs, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch) + _ = assemble_with( + inputs=inputs, + engine=CompilerEngine.NATIVE, + monkeypatch=monkeypatch, + deferred=DEFERRED_PROJECT_FACTS, + ) with pytest.raises(CompileInputError) as native_error: _ = assemble_with( inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch @@ -218,7 +230,10 @@ def test_given_a_variable_only_python_renders_when_assembling_natively_then_pyth ) python: CompiledProject = assemble_with( - inputs=inputs, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch + inputs=inputs, + engine=CompilerEngine.NATIVE, + monkeypatch=monkeypatch, + deferred=DEFERRED_PROJECT_FACTS, ) native: CompiledProject = assemble_with( inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch @@ -259,7 +274,10 @@ def test_given_windows_environment_when_assembling_natively_then_env_lookups_mat ) python: CompiledProject = assemble_with( - inputs=inputs, engine=CompilerEngine.PYTHON, monkeypatch=monkeypatch + inputs=inputs, + engine=CompilerEngine.NATIVE, + monkeypatch=monkeypatch, + deferred=DEFERRED_PROJECT_FACTS, ) native: CompiledProject = assemble_with( inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch @@ -299,8 +317,9 @@ def test_given_sql_analysis_opt_outs_when_attaching_natively_then_python_rejecti python: tuple[dict[str, bool], int] = rejected_opt_outs( project_dir=tmp_path / "python", files=files, - engine=CompilerEngine.PYTHON, + engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch, + deferred=DEFERRED_SYNTAX_CHECK, ) native: tuple[dict[str, bool], int] = rejected_opt_outs( project_dir=tmp_path / "native", diff --git a/tests/integration/src/sqlbuild/compiler/scopes/helpers.py b/tests/integration/src/sqlbuild/compiler/scopes/helpers.py index b747c973b0..3d7d745207 100644 --- a/tests/integration/src/sqlbuild/compiler/scopes/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/scopes/helpers.py @@ -311,7 +311,7 @@ def _expected_model_test(*, index: int, calls: str, expected: list[str]) -> str: def scope_engine_outcomes( *, project_dir: Path, monkeypatch: pytest.MonkeyPatch ) -> tuple[ScopeOutcome, ScopeOutcome, bool]: - """Return the Python and native scope outcomes and whether the native index was built.""" + """Return the shipped and preview scope outcomes and whether the native index was built.""" try: discovered: DiscoveredProjectInputs = discover_project_inputs(project_dir=project_dir) @@ -320,11 +320,11 @@ def scope_engine_outcomes( failure: ScopeOutcome = ScopeOutcome(kind="discovery", value=(), error=str(error)) return failure, failure, True syntax: SqlLexicalSyntax = DuckDbAdapter().sql_lexical_syntax - python: ScopeOutcome = _scope_outcome( + shipped: ScopeOutcome = _scope_outcome( discovered=discovered, macros=macros, syntax=syntax, - engine="python", + engine=CompilerEngine.NATIVE.value, monkeypatch=monkeypatch, ) native: ScopeOutcome = _scope_outcome( @@ -335,19 +335,19 @@ def scope_engine_outcomes( monkeypatch=monkeypatch, ) _ = open_native_scope_index(discovered_inputs=discovered, loaded_macros=macros) - return python, native, True + return shipped, native, True def scope_command_indexes( *, project_dir: Path, monkeypatch: pytest.MonkeyPatch ) -> tuple[ScopeIndex, ScopeIndex]: - """Return the offline `sqb scope` index under the Python and native engines.""" + """Return the offline `sqb scope` index under the shipped and preview engines.""" - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, "python") - python: ScopeIndex = load_or_build_scope_index(project_dir=project_dir, no_cache=True) + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE.value) + shipped: ScopeIndex = load_or_build_scope_index(project_dir=project_dir, no_cache=True) monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE_PREVIEW.value) native: ScopeIndex = load_or_build_scope_index(project_dir=project_dir, no_cache=True) - return python, native + return shipped, native def native_scope_attempts( diff --git a/tests/integration/src/sqlbuild/compiler/scopes/test_native_scope_parity.py b/tests/integration/src/sqlbuild/compiler/scopes/test_native_scope_parity.py index 85375d41ea..50e759cac3 100644 --- a/tests/integration/src/sqlbuild/compiler/scopes/test_native_scope_parity.py +++ b/tests/integration/src/sqlbuild/compiler/scopes/test_native_scope_parity.py @@ -135,13 +135,13 @@ def test_given_scoped_project_when_building_scope_under_each_engine_then_outcome ) -> None: write_project(project_dir=tmp_path, files=test_case.files) - python, native, built = scope_engine_outcomes(project_dir=tmp_path, monkeypatch=monkeypatch) + shipped, native, built = scope_engine_outcomes(project_dir=tmp_path, monkeypatch=monkeypatch) assert ( - native == python, + native == shipped, built, - (python.kind == "error") == bool(test_case.expected_error), - test_case.expected_error in python.error, + (shipped.kind == "error") == bool(test_case.expected_error), + test_case.expected_error in shipped.error, native.grants >= test_case.expected_minimum_grants, ) == (True, True, True, True, True), test_case.description @@ -173,18 +173,18 @@ def test_given_generated_scoped_projects_when_building_scope_then_engines_match( for project_dir in project_dirs ] - python: list[ScopeOutcome] = [outcome[0] for outcome in outcomes] + shipped: list[ScopeOutcome] = [outcome[0] for outcome in outcomes] native: list[ScopeOutcome] = [outcome[1] for outcome in outcomes] assert ( mismatches( inputs=[path.name for path in project_dirs], - expected=[*python], + expected=[*shipped], actual=[*native], ), all(outcome[2] for outcome in outcomes), - sum(item.kind == "ok" for item in python) >= test_case.expected_minimum_valid, - sum(item.kind == "error" for item in python) >= test_case.expected_minimum_invalid, - sum(item.grants > 0 for item in python) >= test_case.expected_minimum_granting, + sum(item.kind == "ok" for item in shipped) >= test_case.expected_minimum_valid, + sum(item.kind == "error" for item in shipped) >= test_case.expected_minimum_invalid, + sum(item.grants > 0 for item in shipped) >= test_case.expected_minimum_granting, ) == (list(test_case.expected_mismatches), True, True, True, True), test_case.description @@ -204,9 +204,9 @@ def test_given_scoped_project_when_scope_command_builds_index_then_engines_match ) -> None: write_project(project_dir=tmp_path, files=test_case.files) - python, native = scope_command_indexes(project_dir=tmp_path, monkeypatch=monkeypatch) + shipped, native = scope_command_indexes(project_dir=tmp_path, monkeypatch=monkeypatch) - assert (native == python, len(native.grants) >= test_case.expected_minimum_grants) == ( + assert (native == shipped, len(native.grants) >= test_case.expected_minimum_grants) == ( True, True, ), test_case.description @@ -215,11 +215,6 @@ def test_given_scoped_project_when_scope_command_builds_index_then_engines_match @pytest.mark.parametrize( "test_case", [ - ScopeEngineTierTestCase( - description="python engine, since the compile scope index is native-only", - engine="python", - expected_native_attempts=1, - ), ScopeEngineTierTestCase( description="shipped native stages", engine="native", expected_native_attempts=1 ), diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 8cbb77988d..d32f88ea10 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -13,6 +13,7 @@ import sqlbuild._native as native_module import sqlbuild.compiler.compile._helpers.assembly.project as assembly_project +import sqlbuild.compiler.compile._helpers.diagnostics.recovery as recovery import sqlbuild.compiler.semantic_checks._helpers.stage as semantic_stage from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter @@ -183,7 +184,7 @@ def captured_semantic_inputs( project_dir: Path, files: dict[str, str], monkeypatch: pytest.MonkeyPatch, - engine: str = "python", + engine: str = "native", ) -> SemanticInputs: """Compile with `engine` and keep the semantic completion stage's inputs.""" @@ -220,10 +221,10 @@ def with_dialect(inputs: SemanticInputs, dialect: str) -> SemanticInputs: def python_completion( *, inputs: SemanticInputs, monkeypatch: pytest.MonkeyPatch ) -> CompiledProject: - """The Python engine's completed project.""" + """The project Python completes when native semantic completion defers.""" with monkeypatch.context() as patch: - patch.setenv(COMPILER_ENGINE_ENV_VAR, "python") + patch.setattr(recovery, "complete_native_semantic_diagnostics", lambda **_kwargs: None) return complete_semantic_diagnostics( project=inputs.project, profile=inputs.profile, @@ -506,18 +507,14 @@ def generated_metadata_files(*, rng: random.Random, model_count: int) -> dict[st return files -def python_metadata( - *, inputs: SemanticInputs, monkeypatch: pytest.MonkeyPatch -) -> tuple[CompilerDiagnostic, ...]: - """The Python engine's metadata diagnostics.""" +def python_metadata(*, inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...]: + """Python's metadata diagnostics, which native semantic completion defers to.""" - with monkeypatch.context() as patch: - patch.setenv(COMPILER_ENGINE_ENV_VAR, "python") - return get_semantic_metadata_diagnostics( - project=inputs.project, - profile=inputs.profile, - resource_sql_analysis=inputs.resource_sql_analysis, - ) + return get_semantic_metadata_diagnostics( + project=inputs.project, + profile=inputs.profile, + resource_sql_analysis=inputs.resource_sql_analysis, + ) def native_metadata(inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...] | None: diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py index 144c7b8c8e..676fedbd41 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py @@ -62,7 +62,7 @@ def test_given_generated_projects_when_checking_metadata_natively_then_matches_p ) for dialect in test_case.dialects: inputs: SemanticInputs = with_dialect(captured, dialect) - expected.append(python_metadata(inputs=inputs, monkeypatch=monkeypatch)) + expected.append(python_metadata(inputs=inputs)) actual.append(native_metadata(inputs)) assert actual == expected diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py index f4abf5a7a3..335268b99e 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py @@ -5,25 +5,6 @@ from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import SqlTestCorpusShape -@dataclass(frozen=True) -class GeneratedSqlTestPlanningParityTestCase: - """Seeded SQL-test projects the native planning glue and the JSON request must agree on.""" - - description: str - seed: int - count: int - test_count: int - shape: SqlTestCorpusShape - adapter_names: tuple[str, ...] - expected_minimum_native_planned: int - expected_minimum_native_with_errors: int - expected_minimum_native_chains: int - expected_minimum_native_raised: int - expected_native_raised_kinds: frozenset[str] - expected_minimum_answered_batches: int - expected_minimum_raised_outcomes: int - - @dataclass(frozen=True) class GeneratedSqlTestAssemblyParityTestCase: """Seeded SQL-test projects native and Python assembly must compile identically.""" diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index 8cc9dbc032..8bf5f4fd43 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -5,7 +5,7 @@ import random from collections import Counter from collections.abc import Callable -from dataclasses import dataclass, replace +from dataclasses import dataclass from itertools import chain, compress from pathlib import Path from typing import cast @@ -13,30 +13,20 @@ import pytest import sqlbuild._native as native_module -from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile._helpers.native_stages import sql_tests as sql_test_stage from sqlbuild.compiler.compile.models import ( - CompiledModelSqlTestPayload, CompiledProject, - CompiledSqlTest, CompileProjectInputs, - CompileSqlTestCte, ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs -from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.pipeline.main.graph import build_project_graph -from sqlbuild.compiler.planner._helpers.sql_tests import native_planning -from sqlbuild.compiler.planner.models import NativeSqlTestArtifact from sqlbuild.compiler.sql_test_glue.models import ( NativeSqlTestAssembly, NativeSqlTestAssemblyRequest, - NativeSqlTestChainRequest, - NativeSqlTestPlanningRequest, ) from sqlbuild.compiler.sql_test_glue.types import ( NativeSqlTestAssemblyRow, - NativeSqlTestPlanRow, ) @@ -185,12 +175,7 @@ class SqlTestCorpusShape: _MOCK_SHARE: float = 0.85 _MACRO_MOCK_SHARE: float = 0.5 _HELPER_SHARE: float = 0.4 -_UNMOCKED_ASSERTION: CompileSqlTestCte = CompileSqlTestCte( - name="__assert__no_returns", sql_body='SELECT order_id FROM __source("raw_returns") WHERE 1 = 0' -) _REGIONS_ASSERTION: str = 'SELECT * FROM __ref("fct_regions") WHERE 1 = 0' -_NATIVE_ERROR_KIND_SEPARATOR: str = ":" -_NATIVE_RAISED_PREFIX: str = "native_raised:" NO_WINDOW: str = "" VALID_WINDOW: str = ', cursor_start "2026-02-01", cursor_end "2026-02-03"' INVERTED_WINDOW: str = ', cursor_start "2026-02-03", cursor_end "2026-02-01"' @@ -402,60 +387,25 @@ def write_project(*, project_dir: Path, files: dict[str, str]) -> None: _ = path.write_text(contents, encoding="utf-8") -def compiled_project(*, project_dir: Path, files: dict[str, str]) -> CompiledProject: - """Write and compile a project, keeping any compile diagnostics on it.""" - - write_project(project_dir=project_dir, files=files) - return build_project_graph( - discovered_inputs=discover_project_inputs(project_dir=project_dir), - adapter=DuckDbAdapter(), - ).project - - -def with_unmocked_assertion(*, project: CompiledProject) -> CompiledSqlTest: - """The first model test with an assertion calling a source nothing mocks. - - The compiler reports such a call itself, so this stands in for a reference it missed and - reaches the planner's own unresolved-reference error. - """ - - test: CompiledSqlTest = next( - filter( - lambda test: isinstance(test.payload, CompiledModelSqlTestPayload), project.sql_tests - ) - ) - payload: CompiledModelSqlTestPayload = cast(CompiledModelSqlTestPayload, test.payload) - return replace( - test, - payload=replace( - payload, - assertion_ctes=(*payload.assertion_ctes, _UNMOCKED_ASSERTION), - authored_ctes=(*payload.authored_ctes, _UNMOCKED_ASSERTION), - ), - ) - +def assembly_outcome(*, project_dir: Path) -> PlanningCallOutcome: + """The compiled SQL tests and diagnostics with native assembly, or what it raises.""" -def use_sql_test_glue(*, monkeypatch: pytest.MonkeyPatch, enabled: bool) -> None: - """Route SQL-test planning through the native glue, or through the JSON request.""" - - monkeypatch.setattr( - native_planning, - "native_stage_enabled", - lambda stage: enabled and stage is NativeStage.SQL_TEST_GLUE, - ) + return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) -def assembly_outcome( - *, project_dir: Path, monkeypatch: pytest.MonkeyPatch, native: bool +def python_assembly_outcome( + *, project_dir: Path, monkeypatch: pytest.MonkeyPatch ) -> PlanningCallOutcome: - """The compiled SQL tests and diagnostics with native or Python assembly, or what it raises.""" - - monkeypatch.setattr( - sql_test_stage, - "native_stage_enabled", - lambda stage: native and stage is NativeStage.SQL_TEST_GLUE, - ) - return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) + """The compiled SQL tests and diagnostics with every test deferred to Python's assembly, or + what it raises.""" + + with monkeypatch.context() as patch: + patch.setattr( + sql_test_stage, + "assemble_native_sql_tests", + lambda *, inputs: (None,) * len(inputs.test_inputs), + ) + return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) def _compiled_tests_and_diagnostics(*, project_dir: Path) -> tuple[object, object]: @@ -504,60 +454,6 @@ def counted_assemblies( return answers -def planning_outcome( - *, - project: CompiledProject, - tests: tuple[CompiledSqlTest, ...], - adapter: BaseAdapter, - render_sql: bool, -) -> PlanningCallOutcome: - """The plans `plan_sql_tests_natively` returns, or what it raises.""" - - return _outcome( - lambda: native_planning.plan_sql_tests_natively( - project=project, - tests=tests, - adapter=adapter, - sql_analysis_enabled=project.settings.sql_analysis, - render_sql=render_sql, - include_plan=not render_sql, - ) - ) - - -def artifact_outcome( - *, - project: CompiledProject, - tests: tuple[CompiledSqlTest, ...], - adapter: BaseAdapter, - glue: bool, -) -> PlanningCallOutcome: - """Rendered artifacts from the glue or from the JSON request, or what planning raises.""" - - plan: Callable[..., tuple[NativeSqlTestArtifact, ...]] = ( - native_planning.plan_and_render_sql_test_artifacts, - native_planning.plan_compiled_sql_test_artifacts, - )[glue] - return _outcome( - lambda: plan( - project=project, - tests=tests, - adapter=adapter, - sql_analysis_enabled=project.settings.sql_analysis, - ) - ) - - -def chain_outcome( - *, project: CompiledProject, tests: tuple[CompiledSqlTest, ...] -) -> PlanningCallOutcome: - """Each test's model chain, or what chain resolution raises.""" - - return _outcome( - lambda: native_planning.resolve_sql_test_model_chains(project=project, tests=tests) - ) - - def outcome_kind(outcome: object) -> str: """`raised` when the call raised, else `answered`; only native rows count as native work.""" @@ -565,62 +461,6 @@ def outcome_kind(outcome: object) -> str: return ("answered", "raised")[called.raised is not None] -def record_native_answers(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count the plans, chains and errors the native glue itself returns for the rest of the test. - - A plan with native error messages counts as `native_with_errors`, any other as - `native_planned`; an error a glue binding raises counts as `native_raised` and under - `native_raised:`. JSON-path planning never reaches these bindings, and errors Python - raises before calling them are never counted. - """ - - answers: Counter[str] = Counter() - plan: Callable[[NativeSqlTestPlanningRequest], tuple[list[NativeSqlTestPlanRow], int, int]] = ( - native_module.plan_compiled_sql_tests - ) - resolve: Callable[[NativeSqlTestChainRequest], list[list[str]]] = ( - native_module.resolve_compiled_sql_test_chains - ) - - def counted_plans( - request: NativeSqlTestPlanningRequest, - ) -> tuple[list[NativeSqlTestPlanRow], int, int]: - response: tuple[list[NativeSqlTestPlanRow], int, int] = _counting_raises( - call=lambda: plan(request), answers=answers - ) - answers.update( - ("native_planned", "native_with_errors")[bool(row[5])] for row in response[0] - ) - return response - - def counted_chains(request: NativeSqlTestChainRequest) -> list[list[str]]: - chains: list[list[str]] = _counting_raises(call=lambda: resolve(request), answers=answers) - answers["native_chains"] += len(chains) - return chains - - monkeypatch.setattr(native_module, "plan_compiled_sql_tests", counted_plans) - monkeypatch.setattr(native_module, "resolve_compiled_sql_test_chains", counted_chains) - return answers - - -def native_raised_kinds(*, answers: Counter[str]) -> frozenset[str]: - """The kinds (message prefixes) of the errors the native glue bindings raised.""" - - return frozenset( - kind.removeprefix(_NATIVE_RAISED_PREFIX) - for kind in filter(lambda kind: kind.startswith(_NATIVE_RAISED_PREFIX), answers) - ) - - -def _counting_raises[T](*, call: Callable[[], T], answers: Counter[str]) -> T: - try: - return call() - except ValueError as error: - answers["native_raised"] += 1 - answers[_NATIVE_RAISED_PREFIX + str(error).partition(_NATIVE_ERROR_KIND_SEPARATOR)[0]] += 1 - raise - - def _outcome(call: Callable[[], object]) -> PlanningCallOutcome: try: return PlanningCallOutcome(value=call(), raised=None) diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py new file mode 100644 index 0000000000..9e6d75b975 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py @@ -0,0 +1,84 @@ +"""SQL tests assembled natively equal Python's assembly, which answers native deferrals.""" + +from __future__ import annotations + +import random +from collections import Counter +from pathlib import Path + +import pytest + +from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.sql_test_glue._test_types import ( + GeneratedSqlTestAssemblyParityTestCase, +) +from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import ( + INVERTED_WINDOW, + NO_WINDOW, + VALID_WINDOW, + PlanningCallOutcome, + SqlTestCorpusShape, + assembly_outcome, + generated_sql_test_assembly_files, + outcome_kind, + python_assembly_outcome, + record_native_assemblies, + write_project, +) + + +@pytest.mark.parametrize( + "test_case", + [ + GeneratedSqlTestAssemblyParityTestCase( + description="model, direct, parameterized and mock-reads-helper tests", + seed=20261010, + count=4, + test_count=12, + shape=SqlTestCorpusShape( + windows=(NO_WINDOW, VALID_WINDOW, INVERTED_WINDOW), + stray_window_share=0.1, + helper_redefinition_share=0.15, + assertion_share=0.2, + unflattenable_assertion_share=0.1, + ), + expected_native_assemblies={ + "native_assembled": 181, + "native_with_diagnostics": 17, + "native_case_fingerprints": 96, + "deferred_macro_mocks": 4, + "deferred_non_ascii_text": 11, + "deferred_decimal_context": 5, + }, + ) + ], + ids=lambda case: case.description, +) +def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_match_python( + test_case: GeneratedSqlTestAssemblyParityTestCase, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + rng: random.Random = random.Random(test_case.seed) + native_assemblies: Counter[str] = record_native_assemblies(monkeypatch=monkeypatch) + differences: list[tuple[object, object, object]] = [] + outcomes: Counter[str] = Counter() + for index in range(test_case.count): + project_dir: Path = tmp_path / f"project_{index}" + write_project( + project_dir=project_dir, + files=generated_sql_test_assembly_files( + rng=rng, test_count=test_case.test_count, shape=test_case.shape + ), + ) + expected: PlanningCallOutcome = python_assembly_outcome( + project_dir=project_dir, monkeypatch=monkeypatch + ) + actual: PlanningCallOutcome = assembly_outcome(project_dir=project_dir) + differences.extend(mismatches(inputs=[index], expected=[expected], actual=[actual])) + outcomes[outcome_kind(actual)] += 1 + + assert (differences, dict(native_assemblies)) == ( + [], + test_case.expected_native_assemblies, + ), outcomes diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py deleted file mode 100644 index 22cc63e2ea..0000000000 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py +++ /dev/null @@ -1,237 +0,0 @@ -"""SQL tests assembled and planned natively equal Python's assembly and the JSON-request plans.""" - -from __future__ import annotations - -import random -from collections import Counter -from pathlib import Path - -import pytest - -from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter -from sqlbuild.adapters.bigquery.classes.bigquery_adapter import BigQueryAdapter -from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter -from sqlbuild.adapters.postgres.classes.postgres_adapter import PostgresAdapter -from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter -from sqlbuild.adapters.sqlserver.classes.sqlserver_adapter import SqlServerAdapter -from sqlbuild.compiler.compile.models import CompiledProject, CompiledSqlTest -from tests.integration.src.sqlbuild.compiler.helpers import mismatches -from tests.integration.src.sqlbuild.compiler.sql_test_glue._test_types import ( - GeneratedSqlTestAssemblyParityTestCase, - GeneratedSqlTestPlanningParityTestCase, -) -from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import ( - INVERTED_WINDOW, - NO_WINDOW, - VALID_WINDOW, - PlanningCallOutcome, - SqlTestCorpusShape, - artifact_outcome, - assembly_outcome, - chain_outcome, - compiled_project, - generated_sql_test_assembly_files, - generated_sql_test_files, - native_raised_kinds, - outcome_kind, - planning_outcome, - record_native_answers, - record_native_assemblies, - use_sql_test_glue, - with_unmocked_assertion, - write_project, -) - -_ADAPTERS: dict[str, BaseAdapter] = { - "duckdb": DuckDbAdapter(), - "postgres": PostgresAdapter(), - "snowflake": SnowflakeAdapter(), - "bigquery": BigQueryAdapter(), - "sqlserver": SqlServerAdapter(), -} - - -@pytest.mark.parametrize( - "test_case", - [ - GeneratedSqlTestPlanningParityTestCase( - description="mocks, expected outputs, assertions, helpers, windows and failing tests", - seed=20261009, - count=4, - test_count=12, - shape=SqlTestCorpusShape( - windows=(NO_WINDOW, VALID_WINDOW, INVERTED_WINDOW), - stray_window_share=0.1, - helper_redefinition_share=0.15, - assertion_share=0.2, - unflattenable_assertion_share=0.1, - ), - adapter_names=("duckdb", "postgres", "snowflake", "bigquery", "sqlserver"), - expected_minimum_native_planned=624, - expected_minimum_native_with_errors=144, - expected_minimum_native_chains=398, - expected_minimum_native_raised=102, - expected_native_raised_kinds=frozenset( - {"compile_input", "planner_input", "sql_test_reference"} - ), - expected_minimum_answered_batches=0, - expected_minimum_raised_outcomes=108, - ), - GeneratedSqlTestPlanningParityTestCase( - description="multi-test batches that plan without window or helper errors", - seed=20261010, - count=3, - test_count=12, - shape=SqlTestCorpusShape( - windows=(NO_WINDOW, VALID_WINDOW), - stray_window_share=0.0, - helper_redefinition_share=0.0, - assertion_share=0.3, - unflattenable_assertion_share=0.0, - ), - adapter_names=("duckdb", "postgres", "snowflake", "bigquery", "sqlserver"), - expected_minimum_native_planned=1068, - expected_minimum_native_with_errors=192, - expected_minimum_native_chains=122, - expected_minimum_native_raised=45, - expected_native_raised_kinds=frozenset({"sql_test_reference"}), - expected_minimum_answered_batches=30, - expected_minimum_raised_outcomes=30, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_sql_tests_when_planning_natively_then_plans_match_the_json_request( - test_case: GeneratedSqlTestPlanningParityTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - rng: random.Random = random.Random(test_case.seed) - native_answers: Counter[str] = record_native_answers(monkeypatch=monkeypatch) - outcomes: Counter[str] = Counter() - differences: list[tuple[object, object, object]] = [] - for index in range(test_case.count): - project: CompiledProject = compiled_project( - project_dir=tmp_path / f"project_{index}", - files=generated_sql_test_files( - rng=rng, test_count=test_case.test_count, shape=test_case.shape - ), - ) - batches: tuple[tuple[CompiledSqlTest, ...], ...] = ( - project.sql_tests, - *((test,) for test in project.sql_tests), - (with_unmocked_assertion(project=project),), - ) - for adapter_name in test_case.adapter_names: - adapter: BaseAdapter = _ADAPTERS[adapter_name] - for render_sql in (False, True): - inputs: list[object] = [] - expected: list[object] = [] - actual: list[object] = [] - for tests in batches: - use_sql_test_glue(monkeypatch=monkeypatch, enabled=False) - expected.append( - planning_outcome( - project=project, tests=tests, adapter=adapter, render_sql=render_sql - ) - ) - use_sql_test_glue(monkeypatch=monkeypatch, enabled=True) - actual.append( - planning_outcome( - project=project, tests=tests, adapter=adapter, render_sql=render_sql - ) - ) - inputs.append((adapter_name, render_sql, tuple(test.name for test in tests))) - differences.extend(mismatches(inputs=inputs, expected=expected, actual=actual)) - outcomes.update(map(outcome_kind, actual[1:])) - outcomes[f"batch_{outcome_kind(actual[0])}"] += 1 - differences.extend( - mismatches( - inputs=[(adapter_name, "artifacts", tests) for tests in batches], - expected=[ - artifact_outcome(project=project, tests=tests, adapter=adapter, glue=False) - for tests in batches - ], - actual=[ - artifact_outcome(project=project, tests=tests, adapter=adapter, glue=True) - for tests in batches - ], - ) - ) - use_sql_test_glue(monkeypatch=monkeypatch, enabled=False) - python_chains: PlanningCallOutcome = chain_outcome(project=project, tests=project.sql_tests) - use_sql_test_glue(monkeypatch=monkeypatch, enabled=True) - native_chains: PlanningCallOutcome = chain_outcome(project=project, tests=project.sql_tests) - differences.extend( - mismatches(inputs=["chains"], expected=[python_chains], actual=[native_chains]) - ) - - assert ( - differences, - native_answers["native_planned"] >= test_case.expected_minimum_native_planned, - native_answers["native_with_errors"] >= test_case.expected_minimum_native_with_errors, - native_answers["native_chains"] >= test_case.expected_minimum_native_chains, - native_answers["native_raised"] >= test_case.expected_minimum_native_raised, - native_raised_kinds(answers=native_answers) >= test_case.expected_native_raised_kinds, - outcomes["batch_answered"] >= test_case.expected_minimum_answered_batches, - outcomes["raised"] >= test_case.expected_minimum_raised_outcomes, - ) == ([], True, True, True, True, True, True, True), (native_answers, outcomes) - - -@pytest.mark.parametrize( - "test_case", - [ - GeneratedSqlTestAssemblyParityTestCase( - description="model, direct, parameterized and mock-reads-helper tests", - seed=20261010, - count=4, - test_count=12, - shape=SqlTestCorpusShape( - windows=(NO_WINDOW, VALID_WINDOW, INVERTED_WINDOW), - stray_window_share=0.1, - helper_redefinition_share=0.15, - assertion_share=0.2, - unflattenable_assertion_share=0.1, - ), - expected_native_assemblies={ - "native_assembled": 181, - "native_with_diagnostics": 17, - "native_case_fingerprints": 96, - "deferred_macro_mocks": 4, - "deferred_non_ascii_text": 11, - "deferred_decimal_context": 5, - }, - ) - ], - ids=lambda case: case.description, -) -def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_match_python( - test_case: GeneratedSqlTestAssemblyParityTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - rng: random.Random = random.Random(test_case.seed) - native_assemblies: Counter[str] = record_native_assemblies(monkeypatch=monkeypatch) - differences: list[tuple[object, object, object]] = [] - outcomes: Counter[str] = Counter() - for index in range(test_case.count): - project_dir: Path = tmp_path / f"project_{index}" - write_project( - project_dir=project_dir, - files=generated_sql_test_assembly_files( - rng=rng, test_count=test_case.test_count, shape=test_case.shape - ), - ) - expected: PlanningCallOutcome = assembly_outcome( - project_dir=project_dir, monkeypatch=monkeypatch, native=False - ) - actual: PlanningCallOutcome = assembly_outcome( - project_dir=project_dir, monkeypatch=monkeypatch, native=True - ) - differences.extend(mismatches(inputs=[index], expected=[expected], actual=[actual])) - outcomes[outcome_kind(actual)] += 1 - - assert (differences, dict(native_assemblies)) == ( - [], - test_case.expected_native_assemblies, - ), outcomes diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py index 44e1fc9f37..b0d576094e 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py @@ -60,7 +60,7 @@ def test_given_corpus_project_when_locating_its_golden_then_only_golden_corpora_ [ GoldenPayloadTestCase( description="paths_ids_timings_and_repeated_sql_masked", - run=engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), + run=engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL), masked_paths=((PROJECT_PATH, ""),), expected_payload={ "commands": [ @@ -118,63 +118,63 @@ def test_given_engine_run_when_building_golden_then_run_specific_noise_is_masked GoldenCheckTestCase( description="identical_engines_match", project="failure/unknown-ref", - recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + recorded=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), checked=( - engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL), + engine_run(engine="native-preview", message=_MESSAGE, compiled_sql=_SQL), ), expected_differences=(), ), GoldenCheckTestCase( description="changed_sql_names_the_engine", project="failure/unknown-ref", - recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + recorded=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), checked=( - engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), - engine_run(engine="native", message=_MESSAGE, compiled_sql="SELECT 2\n"), + engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL), + engine_run(engine="native-preview", message=_MESSAGE, compiled_sql="SELECT 2\n"), ), - expected_differences=(("golden (native)", ("golden", "native")),), + expected_differences=(("golden (native-preview)", ("golden", "native-preview")),), ), GoldenCheckTestCase( description="missing_failure_golden_fails", project="failure/unknown-ref", recorded=(), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(("golden unknown-ref.json", ("golden", "hint")),), ), GoldenCheckTestCase( description="missing_seed_golden_outside_the_recorded_range_is_skipped", project="seed/40", recorded=(), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(), ), GoldenCheckTestCase( description="missing_seed_golden_inside_the_recorded_range_fails", project="seed/3", recorded=(), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(("golden 3.json", ("golden", "hint")),), ), GoldenCheckTestCase( description="missing_dialect_variant_of_the_recorded_first_seed_fails", project="seed/0-postgres", recorded=(), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(("golden 0-postgres.json", ("golden", "hint")),), ), GoldenCheckTestCase( description="dialect_variant_of_an_ad_hoc_first_seed_is_skipped", project="seed/3-postgres", recorded=(), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(), ), GoldenCheckTestCase( description="missing_seed_range_fails_closed_even_with_the_seed_golden", project="seed/3", - recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), - checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + recorded=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(("golden seed_range.toml", ("golden", "hint")),), seed_range_dir="elsewhere", ), diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_evidence.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_evidence.py index d97afea5f4..f9c20b5e25 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_evidence.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_evidence.py @@ -30,7 +30,7 @@ FailureEvidenceTestCase( description="differing warm stderr keeps both engines' evidence", stderr_by_engine={ - "python": "Semantic checks were partial for 1 models:\n", + "native": "Semantic checks were partial for 1 models:\n", "native-preview": "SQL test planning START\n", }, expected_files=( @@ -38,16 +38,16 @@ "example__orders/native-preview/compiled-tests-stat.tsv", "example__orders/native-preview/target/compiled/tests/orders/test_orders.sql", "example__orders/native-preview/target/sql-test-artifacts.json", - "example__orders/python/0-compile-warm.stderr", - "example__orders/python/compiled-tests-stat.tsv", - "example__orders/python/target/compiled/tests/orders/test_orders.sql", - "example__orders/python/target/sql-test-artifacts.json", + "example__orders/native/0-compile-warm.stderr", + "example__orders/native/compiled-tests-stat.tsv", + "example__orders/native/target/compiled/tests/orders/test_orders.sql", + "example__orders/native/target/sql-test-artifacts.json", ), - expected_evidence_engines=("native-preview", "python"), + expected_evidence_engines=("native", "native-preview"), ), FailureEvidenceTestCase( description="identical runs keep nothing", - stderr_by_engine={"python": "done\n", "native-preview": "done\n"}, + stderr_by_engine={"native": "done\n", "native-preview": "done\n"}, expected_files=(), expected_evidence_engines=(), ), @@ -74,7 +74,7 @@ def test_given_engine_runs_when_comparing_then_only_differing_projects_keep_evid source_dir=tmp_path, ), options=DifferentialOptions( - engines=("python", "native-preview"), + engines=("native", "native-preview"), work_dir=tmp_path / "work", jobs=1, stage_captures=False, diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py index 11ac5a0584..60bf52a9f9 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py @@ -2,10 +2,12 @@ from __future__ import annotations +from enum import StrEnum from pathlib import Path import pytest +from scripts.compiler_differential._helpers.running import native_fallbacks from scripts.compiler_differential._helpers.running.native_fallbacks import ( fallback_problems, observed_fallbacks, @@ -49,9 +51,27 @@ ) _SESSION: FallbackKey = ("native-preview", "model_analysis", "analysis_session", "session") _SHIPPED_RUN: RecordedRun = RecordedRun( - engines=("python", "native"), corpora=("seeds", "failures"), seed_start=0, seeds=12 + engines=("native",), corpora=("seeds", "failures"), seed_start=0, seeds=12 ) _LIST_HEAD: str = "seed_start = 0\nseeds = 12\n" +_REFERENCES: FallbackKey = ( + "native", + "reference_extraction", + "reference_extraction.sql_scan", + "deferred", +) +_REFERENCES_ENTRY: str = ( + '\n[[entry]]\nengine = "native"\nstage = "reference_extraction"\n' + 'site = "reference_extraction.sql_scan"\nkind = "deferred"\n' +) + + +class _SwitchableStage(StrEnum): + """Stand-in preview stages: `model_loop` can be switched off, `reference_extraction` cannot.""" + + MODEL_LOOP = "model_loop" + + _VARIABLES_ENTRY: str = ( '\n[[entry]]\nengine = "native"\nstage = "model_loop"\n' 'site = "model_loop.sql_variables"\nkind = "deferred"\n' @@ -89,7 +109,7 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( ) observed: dict[FallbackKey, dict[str, int]] = observed_fallbacks( - comparisons=[comparison], engines=("python", "native-preview") + comparisons=[comparison], engines=("native", "native-preview") ) assert observed == test_case.expected_observed @@ -146,6 +166,16 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( "removes a fallback must report native answers for the work it now does" ], ), + FallbackGateTestCase( + description="vanished_entry_of_a_shipped_stage_only_asks_for_removal", + allow_list=_LIST_HEAD + _REFERENCES_ENTRY + "counts = { seed = 3 }\n", + observed={}, + run=_SHIPPED_RUN, + expected_problems=[ + "native reference_extraction reference_extraction.sql_scan deferred (seed): " + "listed but no longer occurs; remove it from the allow-list" + ], + ), FallbackGateTestCase( description="changed_count_fails", allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "counts = { seed = 195 }\n", @@ -202,9 +232,7 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( description="other_seed_range_is_refused", allow_list=_LIST_HEAD, observed={}, - run=RecordedRun( - engines=("python", "native"), corpora=("seeds",), seed_start=0, seeds=60 - ), + run=RecordedRun(engines=("native",), corpora=("seeds",), seed_start=0, seeds=60), expected_problems=[ "the allow-list holds counts for --seed-start 0 --seeds 12; this run used " "--seed-start 0 --seeds 60" @@ -214,8 +242,9 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( ids=lambda case: case.description, ) def test_given_allow_list_when_checking_a_run_then_every_difference_is_a_problem( - test_case: FallbackGateTestCase, tmp_path: Path + test_case: FallbackGateTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: + monkeypatch.setattr(native_fallbacks, "NativeStage", _SwitchableStage) path: Path = tmp_path / "native_fallbacks.toml" _ = path.write_text(test_case.allow_list, encoding="utf-8") diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_options.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_options.py index 8c7e4d3423..a5ff267939 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_options.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_options.py @@ -25,10 +25,14 @@ [ EngineEnvironmentTestCase( description="grouped_by_engine", - values=("native:PYTHONPATH=/tmp/hook", "native:ORDERS_REGION=a=b", "python:EMPTY="), + values=( + "native:PYTHONPATH=/tmp/hook", + "native:ORDERS_REGION=a=b", + "native-preview:EMPTY=", + ), expected_environment={ "native": {"PYTHONPATH": "/tmp/hook", "ORDERS_REGION": "a=b"}, - "python": {"EMPTY": ""}, + "native-preview": {"EMPTY": ""}, }, ) ], diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py index ffac8427af..4e76f0ada9 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py @@ -44,11 +44,11 @@ expected_deferrals={("legacy_fallback", "compact.py"): 1}, expected_fallbacks={("model_loop.sql_variables", "deferred"): 3}, expected_lines=( - "Polyglot wheel calls (python):", + "Polyglot wheel calls (native):", f" 5 {_SITE} parse_one (seed 5)", - "Analysis deferrals (python):", + "Analysis deferrals (native):", " 1 legacy_fallback compact.py (seed 1)", - "Native-to-Python fallbacks (python):", + "Native-to-Python fallbacks (native):", " 3 model_loop.sql_variables deferred (seed 3)", "Polyglot wheel calls (native-preview): none recorded", "Analysis deferrals (native-preview): none recorded", @@ -77,7 +77,7 @@ def test_given_record_files_when_reporting_then_counts_are_summed_per_engine_and records=(records, read_analysis_records(tmp_path / "missing")), ) ], - engines=("python", "native-preview"), + engines=("native", "native-preview"), ) ) diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py index cd1c3f8ee6..ff29b155c4 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py @@ -130,7 +130,7 @@ def test_given_results_and_coverage_when_summarizing_then_failures_never_read_as ) -> None: summary: str = format_summary( comparisons=test_case.comparisons, - engines=("python", "native"), + engines=("native", "native-preview"), seconds=2.0, missing_coverage=test_case.missing_coverage, gate_failures=test_case.gate_failures, diff --git a/tests/unit/src/sqlbuild/cli/commands/_helpers/entry/test_compiler_engine_option.py b/tests/unit/src/sqlbuild/cli/commands/_helpers/entry/test_compiler_engine_option.py index fc4a0629f3..468456a3a7 100644 --- a/tests/unit/src/sqlbuild/cli/commands/_helpers/entry/test_compiler_engine_option.py +++ b/tests/unit/src/sqlbuild/cli/commands/_helpers/entry/test_compiler_engine_option.py @@ -38,15 +38,15 @@ ), CompilerEngineOptionTestCase( description="flag_after_subcommand", - argv=("plan", "--compiler-engine", "python"), + argv=("plan", "--compiler-engine", "native"), environment_value="", - expected_engine="python", + expected_engine="native", ), CompilerEngineOptionTestCase( description="flag_overrides_invalid_environment", - argv=("compile", "--compiler-engine", "python"), + argv=("compile", "--compiler-engine", "native-preview"), environment_value="rust", - expected_engine="python", + expected_engine="native-preview", ), ], ids=lambda case: case.description, @@ -72,7 +72,21 @@ def test_given_engine_selection_when_parsing_then_flag_value_is_recorded( argv=("compile", "--compiler-engine", "rust"), environment_value="", expected_exit_code=2, - expected_error="argument --compiler-engine: invalid choice: 'rust'", + expected_error="--compiler-engine must be one of native, native-preview (got 'rust')", + ), + RejectedCompilerEngineTestCase( + description="removed_python_flag_value", + argv=("compile", "--compiler-engine", "python"), + environment_value="", + expected_exit_code=2, + expected_error="--compiler-engine 'python' is no longer supported", + ), + RejectedCompilerEngineTestCase( + description="removed_python_environment_value", + argv=("compile",), + environment_value="python", + expected_exit_code=2, + expected_error="SQLBUILD_COMPILER_ENGINE 'python' is no longer supported", ), RejectedCompilerEngineTestCase( description="unknown_environment_value", @@ -80,8 +94,7 @@ def test_given_engine_selection_when_parsing_then_flag_value_is_recorded( environment_value="rust", expected_exit_code=2, expected_error=( - "SQLBUILD_COMPILER_ENGINE must be one of python, native, native-preview " - "(got 'rust')" + "SQLBUILD_COMPILER_ENGINE must be one of native, native-preview (got 'rust')" ), ), ], diff --git a/tests/unit/src/sqlbuild/cli/compile_reuse/_helpers/test_compiler_engine_reuse_key.py b/tests/unit/src/sqlbuild/cli/compile_reuse/_helpers/test_compiler_engine_reuse_key.py index e122193509..76965985e1 100644 --- a/tests/unit/src/sqlbuild/cli/compile_reuse/_helpers/test_compiler_engine_reuse_key.py +++ b/tests/unit/src/sqlbuild/cli/compile_reuse/_helpers/test_compiler_engine_reuse_key.py @@ -25,15 +25,15 @@ "test_case", [ EngineReuseDigestTestCase( - description="python_then_native", - first_environment={COMPILER_ENGINE_ENV_VAR: "python"}, + description="preview_then_native", + first_environment={COMPILER_ENGINE_ENV_VAR: "native-preview"}, second_environment={COMPILER_ENGINE_ENV_VAR: "native"}, expected_same_digest=False, ), EngineReuseDigestTestCase( - description="unset_then_python", + description="unset_then_preview", first_environment={}, - second_environment={COMPILER_ENGINE_ENV_VAR: "python"}, + second_environment={COMPILER_ENGINE_ENV_VAR: "native-preview"}, expected_same_digest=False, ), EngineReuseDigestTestCase( @@ -74,9 +74,9 @@ def test_given_same_invocation_when_engine_environment_changes_then_reuse_key_fo "test_case", [ EngineReuseStoreTestCase( - description="python", - engine="python", - expected_entry_directory="target/cache/compiler/project-reuse-v1", + description="native_preview", + engine="native-preview", + expected_entry_directory="target/cache/compiler-native-preview-v1/project-reuse-v1", ), EngineReuseStoreTestCase( description="native", diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/config/helpers.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/config/helpers.py index 7e4ceef286..d6cec06921 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/config/helpers.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/config/helpers.py @@ -25,8 +25,6 @@ ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs, DiscoveredSqlModelFile -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR -from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.spec.contracts.models import ( DefaultsConfig, MaterializationDefaultsConfig, @@ -46,9 +44,8 @@ def compile_with_config_build_count( project_dir: Path, monkeypatch: pytest.MonkeyPatch, ) -> tuple[CompileProjectInputs, int]: - """Compile a project on the Python engine while counting its native model config builds.""" + """Compile a project while counting its native model config builds.""" - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.PYTHON.value) build_counts: list[int] = [0] original_build_model_config: Callable[..., CompileModelConfig] = ( attachment_core.build_native_model_config diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py index 1033e715b4..3213343f46 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py @@ -49,7 +49,7 @@ stored_analysis_contents, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("python_compiler_engine") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") _CACHE_REPO_FILES: dict[str, str] = { "sqlbuild_project.toml": 'name = "cache_demo"\nadapter = "duckdb"\n', diff --git a/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py b/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py index 51aa39f3d7..5754853221 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py @@ -9,7 +9,6 @@ CompilerEngine, CompilerStage, NativeFallbackSite, - NativeStage, ) @@ -121,35 +120,6 @@ class SharedCaptureTestCase: expected_shared_nodes: int -@dataclass(frozen=True) -class NativeStageTierTestCase: - """One engine, one native stage, and whether that engine runs it.""" - - description: str - engine: CompilerEngine - stage: NativeStage - expected_enabled: bool - - -@dataclass(frozen=True) -class DefaultEngineStageTestCase: - """With no engine selected, the native stages the default compile runs and those it skips.""" - - description: str - expected_enabled: frozenset[NativeStage] - expected_disabled: frozenset[NativeStage] - - -@dataclass(frozen=True) -class NativeStageCouplingTestCase: - """A native stage that may only run where the stage it relies on also runs natively.""" - - description: str - dependent: NativeStage - dependency: NativeStage - expected_engines_without_dependency: frozenset[CompilerEngine] - - @dataclass(frozen=True) class NativeFallbackRecorderTestCase: """Fallbacks one process records and the record file it writes at exit.""" diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_cache_namespace.py b/tests/unit/src/sqlbuild/compiler/frontier/test_cache_namespace.py index 8f284670f9..eee5cacf38 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/test_cache_namespace.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/test_cache_namespace.py @@ -1,4 +1,4 @@ -"""On-disk compiler stores are namespaced by engine; Python keeps its existing paths.""" +"""On-disk compiler stores are namespaced by engine.""" from __future__ import annotations @@ -19,12 +19,6 @@ @pytest.mark.parametrize( "test_case", [ - EngineCacheNameTestCase( - description="python_keeps_name", - engine=CompilerEngine.PYTHON, - base="compiler", - expected_name="compiler", - ), EngineCacheNameTestCase( description="native_gets_versioned_suffix", engine=CompilerEngine.NATIVE, @@ -57,16 +51,6 @@ def test_given_engine_when_naming_store_then_only_native_engines_are_suffixed( @pytest.mark.parametrize( "test_case", [ - EngineStorePathsTestCase( - description="python_paths_unchanged", - engine=CompilerEngine.PYTHON, - expected_paths=( - "target/cache/compiler", - "target/cache/compiler", - "target/cache/compiler/declaration-scopes-v2", - "target/rules-cache/bulk/sql.json", - ), - ), EngineStorePathsTestCase( description="native_paths_separate", engine=CompilerEngine.NATIVE, diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_native_stage_tiers.py b/tests/unit/src/sqlbuild/compiler/frontier/test_native_stage_tiers.py deleted file mode 100644 index e004764d10..0000000000 --- a/tests/unit/src/sqlbuild/compiler/frontier/test_native_stage_tiers.py +++ /dev/null @@ -1,408 +0,0 @@ -"""Each engine runs exactly the native stages whose registered tier it includes.""" - -from __future__ import annotations - -from itertools import compress - -import pytest - -from sqlbuild.compiler.frontier.constants import ( - COMPILER_ENGINE_ENV_VAR, - ENGINE_NATIVE_STAGE_TIERS, - NATIVE_STAGE_TIERS, -) -from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import CompilerEngine, NativeStage -from tests.unit.src.sqlbuild.compiler.frontier._test_types import ( - DefaultEngineStageTestCase, - NativeStageCouplingTestCase, - NativeStageTierTestCase, -) - - -@pytest.mark.parametrize( - "test_case", - [ - NativeStageTierTestCase( - description="python_discovery", - engine=CompilerEngine.PYTHON, - stage=NativeStage.DISCOVERY, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="python_declaration_scopes", - engine=CompilerEngine.PYTHON, - stage=NativeStage.DECLARATION_SCOPES, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_discovery", - engine=CompilerEngine.NATIVE, - stage=NativeStage.DISCOVERY, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_declaration_scopes", - engine=CompilerEngine.NATIVE, - stage=NativeStage.DECLARATION_SCOPES, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_discovery", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.DISCOVERY, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_declaration_scopes", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.DECLARATION_SCOPES, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_model_config", - engine=CompilerEngine.PYTHON, - stage=NativeStage.MODEL_CONFIG, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_model_config", - engine=CompilerEngine.NATIVE, - stage=NativeStage.MODEL_CONFIG, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_model_config", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.MODEL_CONFIG, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_reference_extraction", - engine=CompilerEngine.PYTHON, - stage=NativeStage.REFERENCE_EXTRACTION, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_reference_extraction", - engine=CompilerEngine.NATIVE, - stage=NativeStage.REFERENCE_EXTRACTION, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_reference_extraction", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.REFERENCE_EXTRACTION, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_declaration_files", - engine=CompilerEngine.PYTHON, - stage=NativeStage.DECLARATION_FILES, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_declaration_files", - engine=CompilerEngine.NATIVE, - stage=NativeStage.DECLARATION_FILES, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_declaration_files", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.DECLARATION_FILES, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_model_loop", - engine=CompilerEngine.PYTHON, - stage=NativeStage.MODEL_LOOP, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_model_loop", - engine=CompilerEngine.NATIVE, - stage=NativeStage.MODEL_LOOP, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_model_loop", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.MODEL_LOOP, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_macro_calls", - engine=CompilerEngine.PYTHON, - stage=NativeStage.MACRO_CALLS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="python_macro_call_store", - engine=CompilerEngine.PYTHON, - stage=NativeStage.MACRO_CALL_STORE, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_macro_calls", - engine=CompilerEngine.NATIVE, - stage=NativeStage.MACRO_CALLS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_macro_call_store", - engine=CompilerEngine.NATIVE, - stage=NativeStage.MACRO_CALL_STORE, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_macro_calls", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.MACRO_CALLS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_macro_call_store", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.MACRO_CALL_STORE, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_attachments", - engine=CompilerEngine.PYTHON, - stage=NativeStage.ATTACHMENTS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_attachments", - engine=CompilerEngine.NATIVE, - stage=NativeStage.ATTACHMENTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_attachments", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.ATTACHMENTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_type_system", - engine=CompilerEngine.PYTHON, - stage=NativeStage.TYPE_SYSTEM, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_type_system", - engine=CompilerEngine.NATIVE, - stage=NativeStage.TYPE_SYSTEM, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_type_system", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.TYPE_SYSTEM, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_model_analysis", - engine=CompilerEngine.PYTHON, - stage=NativeStage.MODEL_ANALYSIS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_model_analysis", - engine=CompilerEngine.NATIVE, - stage=NativeStage.MODEL_ANALYSIS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_model_analysis", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.MODEL_ANALYSIS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_semantic_checks", - engine=CompilerEngine.PYTHON, - stage=NativeStage.SEMANTIC_CHECKS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_semantic_checks", - engine=CompilerEngine.NATIVE, - stage=NativeStage.SEMANTIC_CHECKS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_semantic_checks", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.SEMANTIC_CHECKS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_contracts", - engine=CompilerEngine.PYTHON, - stage=NativeStage.CONTRACTS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_contracts", - engine=CompilerEngine.NATIVE, - stage=NativeStage.CONTRACTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_contracts", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.CONTRACTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_lineage_facts", - engine=CompilerEngine.PYTHON, - stage=NativeStage.LINEAGE_FACTS, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_lineage_facts", - engine=CompilerEngine.NATIVE, - stage=NativeStage.LINEAGE_FACTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_lineage_facts", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.LINEAGE_FACTS, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_sql_test_glue", - engine=CompilerEngine.PYTHON, - stage=NativeStage.SQL_TEST_GLUE, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_sql_test_glue", - engine=CompilerEngine.NATIVE, - stage=NativeStage.SQL_TEST_GLUE, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_sql_test_glue", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.SQL_TEST_GLUE, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="python_project_assembly", - engine=CompilerEngine.PYTHON, - stage=NativeStage.PROJECT_ASSEMBLY, - expected_enabled=False, - ), - NativeStageTierTestCase( - description="native_project_assembly", - engine=CompilerEngine.NATIVE, - stage=NativeStage.PROJECT_ASSEMBLY, - expected_enabled=True, - ), - NativeStageTierTestCase( - description="native_preview_project_assembly", - engine=CompilerEngine.NATIVE_PREVIEW, - stage=NativeStage.PROJECT_ASSEMBLY, - expected_enabled=True, - ), - ], - ids=lambda case: case.description, -) -def test_given_engine_when_checking_native_stage_then_only_its_tiers_run( - test_case: NativeStageTierTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, test_case.engine.value) - - assert native_stage_enabled(test_case.stage) is test_case.expected_enabled - - -@pytest.mark.parametrize( - "test_case", - [ - DefaultEngineStageTestCase( - description="discovery_rendering_and_analysis_native", - expected_enabled=frozenset( - { - NativeStage.DISCOVERY, - NativeStage.DECLARATION_SCOPES, - NativeStage.MODEL_CONFIG, - NativeStage.REFERENCE_EXTRACTION, - NativeStage.DECLARATION_FILES, - NativeStage.MODEL_LOOP, - NativeStage.MACRO_CALLS, - NativeStage.MACRO_CALL_STORE, - NativeStage.ATTACHMENTS, - NativeStage.TYPE_SYSTEM, - NativeStage.MODEL_ANALYSIS, - NativeStage.SEMANTIC_CHECKS, - NativeStage.CONTRACTS, - NativeStage.LINEAGE_FACTS, - NativeStage.SQL_TEST_GLUE, - NativeStage.PROJECT_ASSEMBLY, - } - ), - expected_disabled=frozenset(), - ) - ], - ids=lambda case: case.description, -) -def test_given_no_engine_selection_when_checking_native_stages_then_every_stage_runs_natively( - test_case: DefaultEngineStageTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - monkeypatch.delenv(COMPILER_ENGINE_ENV_VAR, raising=False) - - assert {stage: native_stage_enabled(stage) for stage in NativeStage} == { - **dict.fromkeys(test_case.expected_enabled, True), - **dict.fromkeys(test_case.expected_disabled, False), - } - - -@pytest.mark.parametrize( - "test_case", - [ - NativeStageCouplingTestCase( - description=( - "semantic checks normalize types with the native type system unconditionally, " - "while Python only does so where the type system stage runs natively" - ), - dependent=NativeStage.SEMANTIC_CHECKS, - dependency=NativeStage.TYPE_SYSTEM, - expected_engines_without_dependency=frozenset(), - ), - ], - ids=lambda case: case.description, -) -def test_given_coupled_native_stages_when_reading_tiers_then_dependent_never_runs_alone( - test_case: NativeStageCouplingTestCase, -) -> None: - engines: tuple[CompilerEngine, ...] = tuple(ENGINE_NATIVE_STAGE_TIERS) - dependent_engines: frozenset[CompilerEngine] = frozenset( - compress( - engines, - ( - NATIVE_STAGE_TIERS[test_case.dependent] in tiers - for tiers in ENGINE_NATIVE_STAGE_TIERS.values() - ), - ) - ) - dependency_engines: frozenset[CompilerEngine] = frozenset( - compress( - engines, - ( - NATIVE_STAGE_TIERS[test_case.dependency] in tiers - for tiers in ENGINE_NATIVE_STAGE_TIERS.values() - ), - ) - ) - - assert dependent_engines - dependency_engines == (test_case.expected_engines_without_dependency) - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_resolve_compiler_engine.py b/tests/unit/src/sqlbuild/compiler/frontier/test_resolve_compiler_engine.py index 3180d6d89e..1f4e46a8e8 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/test_resolve_compiler_engine.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/test_resolve_compiler_engine.py @@ -24,9 +24,6 @@ EngineResolutionTestCase( description="empty", raw_value="", expected_engine=CompilerEngine.NATIVE ), - EngineResolutionTestCase( - description="python", raw_value="python", expected_engine=CompilerEngine.PYTHON - ), EngineResolutionTestCase( description="native", raw_value="native", expected_engine=CompilerEngine.NATIVE ), @@ -70,32 +67,39 @@ def test_given_no_engine_variable_when_resolving_then_native_is_the_default( description="unknown", raw_value="rust", expected_message=( - "SQLBUILD_COMPILER_ENGINE must be one of python, native, native-preview " - "(got 'rust')" + "SQLBUILD_COMPILER_ENGINE must be one of native, native-preview (got 'rust')" ), ), EngineErrorTestCase( description="wrong_case", raw_value="Native", expected_message=( - "SQLBUILD_COMPILER_ENGINE must be one of python, native, native-preview " - "(got 'Native')" + "SQLBUILD_COMPILER_ENGINE must be one of native, native-preview (got 'Native')" ), ), EngineErrorTestCase( description="underscored_preview", raw_value="native_preview", expected_message=( - "SQLBUILD_COMPILER_ENGINE must be one of python, native, native-preview " + "SQLBUILD_COMPILER_ENGINE must be one of native, native-preview " "(got 'native_preview')" ), ), + EngineErrorTestCase( + description="removed_python", + raw_value="python", + expected_message=( + "SQLBUILD_COMPILER_ENGINE 'python' is no longer supported: SQLBuild removed its " + "Python compiler, and the native compiler is the default. Unset " + "SQLBUILD_COMPILER_ENGINE and drop --compiler-engine to use it; to run the " + "Python compiler for comparison, pin an earlier SQLBuild release." + ), + ), EngineErrorTestCase( description="padded", raw_value=" python", expected_message=( - "SQLBUILD_COMPILER_ENGINE must be one of python, native, native-preview " - "(got ' python')" + "SQLBUILD_COMPILER_ENGINE must be one of native, native-preview (got ' python')" ), ), ], @@ -116,11 +120,11 @@ def test_given_unknown_engine_when_resolving_then_error_names_accepted_and_curre "test_case", [ EngineOverrideTestCase( - description="python_restored", - previous="python", + description="preview_restored", + previous="native-preview", override=CompilerEngine.NATIVE, expected_inside=CompilerEngine.NATIVE, - expected_after="python", + expected_after="native-preview", ), EngineOverrideTestCase( description="empty_restored", diff --git a/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/helpers.py b/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/helpers.py index 295fcc27ba..03f3af6119 100644 --- a/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/helpers.py +++ b/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/helpers.py @@ -42,7 +42,7 @@ from sqlbuild.compiler.planner._helpers.sql_tests.assembly import plan_sql_tests from sqlbuild.compiler.planner._helpers.sql_tests.comments import uncommented_pattern_matches from sqlbuild.compiler.planner._helpers.sql_tests.native_planning import ( - plan_and_render_sql_test_artifacts, + plan_compiled_sql_test_artifacts, plan_sql_tests_natively, sql_test_plan_error_messages, ) @@ -142,7 +142,7 @@ def assert_native_artifact_matches_runtime_plan( set_difference_operator=adapter.render_set_difference_operator(), sql_analysis_dialect=adapter.sql_analysis_dialect(), ) - (artifact,) = plan_and_render_sql_test_artifacts( + (artifact,) = plan_compiled_sql_test_artifacts( project=project, tests=(sql_test,), adapter=adapter, diff --git a/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/test_plan_test.py b/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/test_plan_test.py index be518b4b4f..789aedcede 100644 --- a/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/test_plan_test.py +++ b/tests/unit/src/sqlbuild/compiler/planner/_helpers/sql_test_assembly/test_plan_test.py @@ -22,7 +22,7 @@ from sqlbuild.compiler.compile.types import CompiledResourceType, SqlTestMode from sqlbuild.compiler.discovery.models import DiscoveredSqlTestBlock, DiscoveredSqlTestFile from sqlbuild.compiler.planner._helpers.sql_tests.native_planning import ( - plan_and_render_sql_test_artifacts, + plan_compiled_sql_test_artifacts, sql_test_plan_error_messages, ) from sqlbuild.compiler.planner.exceptions import PlannerInputError @@ -1339,7 +1339,7 @@ def test_given_sql_analysis_enabled_when_generated_cte_name_conflicts_then_it_ra sql_analysis_enabled=True, ) with pytest.raises(CompileInputError, match=test_case.expected_error_fragments[0]): - plan_and_render_sql_test_artifacts( + plan_compiled_sql_test_artifacts( project=project, tests=(compiled_test,), adapter=DuckDbAdapter(), @@ -1606,7 +1606,7 @@ def test_given_reference_the_compiler_missed_when_planning_then_it_fails_with_lo compiled_test, project = build_test_and_project(test_case) with pytest.raises(SqlTestReferenceError) as raised: - plan_and_render_sql_test_artifacts( + plan_compiled_sql_test_artifacts( project=project, tests=(compiled_test,), adapter=DuckDbAdapter(), From b3d560570a348f042a3a6190b5d490b0882f5933 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:21:55 +0100 Subject: [PATCH 03/36] chore(compiler): regenerate the fallback allow-list without native answer counts The shipped stages no longer report native answers, so their .native entries leave the allow-list. No fallback entry or count changes and every golden is unchanged. --- scripts/compiler_differential/README.md | 9 +- .../native_fallbacks.toml | 490 ------------------ 2 files changed, 5 insertions(+), 494 deletions(-) diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md index f31d1410a9..4acdbf9eba 100644 --- a/scripts/compiler_differential/README.md +++ b/scripts/compiler_differential/README.md @@ -22,9 +22,10 @@ and of the work each native stage answers itself. It covers three kinds of recor path where a native stage hands work to Python, or runs Python with no native path yet; - analysis-stage deferrals: every `analysis-deferrals-*.jsonl` record (analysis session, lineage, semantic checks, contracts); -- native answers, from `report_native_answer(stage=NativeStage., kind=...)` where a - preview stage's native path produced the result: files, models, scans or calls. Their site is - `.native`. +- native answers, from `report_native_answer(stage=NativeStage., kind=...)` + (`frontier/main/_report_native_answer.py`) where a preview stage's native path produced the + result: files, models, scans or calls. Their site is `.native`. No stage is in preview + today, so the list holds no answer entries. Counting only fallbacks cannot tell a stage that answers natively from one that no longer runs natively at all. The answer counts can: if a preview stage is switched off, its `.native` @@ -86,7 +87,7 @@ in the same PR as the change that caused it. same diff: the port must report the work it now answers. A fallback that vanished with no new answers is a stage switched off, not a port; the gate refuses it. - Code: every new path that hands work to Python calls `report_native_fallback`, and every new - native path calls `report_native_answer` where its result is used. + preview-stage native path calls `report_native_answer` where its result is used. - Goldens: - Every changed golden is an intended output change, named in the PR body. - An output-neutral port changes no golden. diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index dcab198a7f..5452d4a487 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -27,118 +27,6 @@ site = "sql_test_assembly" kind = "macro_mocks" counts = { seed = 29 } -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "audit_renderings" -counts = { example = 120, failure = 10, fixture = 112, seed = 370 } - -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 179, fixture = 156, seed = 961 } - -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "function_headers" -counts = { example = 8, failure = 13, fixture = 12, seed = 102 } - -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "omitted_select_scans" -counts = { example = 24, failure = 22, fixture = 32, seed = 193 } - -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "scenario_ctes" -counts = { example = 8, failure = 7, fixture = 12, seed = 37 } - -[[entry]] -engine = "native" -stage = "attachments" -site = "attachments.native" -kind = "seed_pairs" -counts = { example = 8, fixture = 4, seed = 26 } - -[[entry]] -engine = "native" -stage = "contracts" -site = "contracts.native" -kind = "contract_validations" -counts = { example = 9, failure = 113, fixture = 6, seed = 25 } - -[[entry]] -engine = "native" -stage = "contracts" -site = "contracts.native" -kind = "promotion_checks" -counts = { example = 12, failure = 113, fixture = 8, seed = 33 } - -[[entry]] -engine = "native" -stage = "declaration_files" -site = "declaration_files.native" -kind = "failed_files" -counts = { failure = 10 } - -[[entry]] -engine = "native" -stage = "declaration_files" -site = "declaration_files.native" -kind = "parsed_files" -counts = { example = 32, failure = 83, fixture = 20, seed = 799 } - -[[entry]] -engine = "native" -stage = "declaration_files" -site = "declaration_files.native" -kind = "seed_files" -counts = { example = 8, failure = 3, fixture = 4, seed = 43 } - -[[entry]] -engine = "native" -stage = "declaration_scopes" -site = "declaration_scopes.native" -kind = "relationship_scans" -counts = { example = 15, failure = 6, fixture = 18, seed = 130 } - -[[entry]] -engine = "native" -stage = "declaration_scopes" -site = "declaration_scopes.native" -kind = "scope_indexes" -counts = { example = 12, failure = 258, fixture = 12, seed = 44 } - -[[entry]] -engine = "native" -stage = "discovery" -site = "discovery.native" -kind = "model_file_listings" -counts = { example = 12, failure = 326, fixture = 12, seed = 50 } - -[[entry]] -engine = "native" -stage = "lineage_facts" -site = "lineage_facts.native" -kind = "lineage_graphs" -counts = { example = 9, failure = 113, fixture = 6, seed = 22 } - -[[entry]] -engine = "native" -stage = "macro_call_store" -site = "macro_call_store.native" -kind = "served_calls" -counts = { example = 18, fixture = 15, seed = 192 } - [[entry]] engine = "native" stage = "macro_calls" @@ -146,20 +34,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native" -stage = "macro_calls" -site = "macro_calls.native" -kind = "bridged_calls" -counts = { example = 32, failure = 9, fixture = 28, seed = 719 } - -[[entry]] -engine = "native" -stage = "macro_calls" -site = "macro_calls.native" -kind = "parsed_arguments" -counts = { example = 6, failure = 16, fixture = 5, seed = 369 } - [[entry]] engine = "native" stage = "macro_calls" @@ -167,90 +41,6 @@ site = "macro_calls.resolution" kind = "deferred" counts = { failure = 4, seed = 3 } -[[entry]] -engine = "native" -stage = "model_analysis" -site = "model_analysis.native" -kind = "dynamic_contract_proofs" -counts = { seed = 3 } - -[[entry]] -engine = "native" -stage = "model_analysis" -site = "model_analysis.native" -kind = "expression_shapes" -counts = { example = 4, failure = 119, fixture = 4, seed = 100 } - -[[entry]] -engine = "native" -stage = "model_analysis" -site = "model_analysis.native" -kind = "model_analyses" -counts = { example = 56, failure = 232, fixture = 52, seed = 698 } - -[[entry]] -engine = "native" -stage = "model_config" -site = "model_config.native" -kind = "config_builds" -counts = { example = 56, failure = 347, fixture = 64, seed = 914 } - -[[entry]] -engine = "native" -stage = "model_config" -site = "model_config.native" -kind = "effective_vars" -counts = { example = 29, failure = 273, fixture = 15, seed = 121 } - -[[entry]] -engine = "native" -stage = "model_config" -site = "model_config.native" -kind = "header_metadata_parses" -counts = { example = 56, failure = 402, fixture = 60, seed = 1116 } - -[[entry]] -engine = "native" -stage = "model_config" -site = "model_config.native" -kind = "template_expansions" -counts = { example = 214, failure = 1657, fixture = 333, seed = 1471 } - -[[entry]] -engine = "native" -stage = "model_config" -site = "model_config.native" -kind = "validations" -counts = { example = 56, failure = 441, fixture = 64, seed = 1124 } - -[[entry]] -engine = "native" -stage = "model_loop" -site = "model_loop.native" -kind = "cursor_intrinsic_validations" -counts = { example = 56, failure = 448, fixture = 64, seed = 1926 } - -[[entry]] -engine = "native" -stage = "model_loop" -site = "model_loop.native" -kind = "declaration_contexts" -counts = { example = 64, failure = 136, fixture = 60, seed = 964 } - -[[entry]] -engine = "native" -stage = "model_loop" -site = "model_loop.native" -kind = "declaration_expansions" -counts = { example = 56, failure = 468, fixture = 64, seed = 1127 } - -[[entry]] -engine = "native" -stage = "model_loop" -site = "model_loop.native" -kind = "variable_substitutions" -counts = { example = 56, failure = 522, fixture = 72, seed = 1187 } - [[entry]] engine = "native" stage = "project_assembly" @@ -258,27 +48,6 @@ site = "project_assembly.assembly" kind = "deferred" counts = { failure = 4 } -[[entry]] -engine = "native" -stage = "project_assembly" -site = "project_assembly.native" -kind = "project_assemblies" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } - -[[entry]] -engine = "native" -stage = "reference_extraction" -site = "reference_extraction.native" -kind = "reference_scans" -counts = { example = 250, failure = 512, fixture = 249, seed = 1532 } - -[[entry]] -engine = "native" -stage = "semantic_checks" -site = "semantic_checks.native" -kind = "semantic_completions" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } - [[entry]] engine = "native" stage = "sql_test_glue" @@ -286,20 +55,6 @@ site = "sql_test_glue.assembly" kind = "deferred" counts = { seed = 29 } -[[entry]] -engine = "native" -stage = "sql_test_glue" -site = "sql_test_glue.native" -kind = "sql_test_assemblies" -counts = { example = 16, failure = 7, fixture = 20, seed = 143 } - -[[entry]] -engine = "native" -stage = "sql_test_glue" -site = "sql_test_glue.native" -kind = "sql_test_plans" -counts = { example = 1, failure = 5, fixture = 1, seed = 9 } - [[entry]] engine = "native-preview" stage = "analysis" @@ -321,118 +76,6 @@ site = "sql_test_assembly" kind = "macro_mocks" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "audit_renderings" -counts = { example = 120, failure = 10, fixture = 112, seed = 370 } - -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 179, fixture = 156, seed = 961 } - -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "function_headers" -counts = { example = 8, failure = 13, fixture = 12, seed = 102 } - -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "omitted_select_scans" -counts = { example = 24, failure = 22, fixture = 32, seed = 193 } - -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "scenario_ctes" -counts = { example = 8, failure = 7, fixture = 12, seed = 37 } - -[[entry]] -engine = "native-preview" -stage = "attachments" -site = "attachments.native" -kind = "seed_pairs" -counts = { example = 8, fixture = 4, seed = 26 } - -[[entry]] -engine = "native-preview" -stage = "contracts" -site = "contracts.native" -kind = "contract_validations" -counts = { example = 9, failure = 113, fixture = 6, seed = 25 } - -[[entry]] -engine = "native-preview" -stage = "contracts" -site = "contracts.native" -kind = "promotion_checks" -counts = { example = 12, failure = 113, fixture = 8, seed = 33 } - -[[entry]] -engine = "native-preview" -stage = "declaration_files" -site = "declaration_files.native" -kind = "failed_files" -counts = { failure = 10 } - -[[entry]] -engine = "native-preview" -stage = "declaration_files" -site = "declaration_files.native" -kind = "parsed_files" -counts = { example = 32, failure = 83, fixture = 20, seed = 799 } - -[[entry]] -engine = "native-preview" -stage = "declaration_files" -site = "declaration_files.native" -kind = "seed_files" -counts = { example = 8, failure = 3, fixture = 4, seed = 43 } - -[[entry]] -engine = "native-preview" -stage = "declaration_scopes" -site = "declaration_scopes.native" -kind = "relationship_scans" -counts = { example = 15, failure = 6, fixture = 18, seed = 130 } - -[[entry]] -engine = "native-preview" -stage = "declaration_scopes" -site = "declaration_scopes.native" -kind = "scope_indexes" -counts = { example = 12, failure = 258, fixture = 12, seed = 44 } - -[[entry]] -engine = "native-preview" -stage = "discovery" -site = "discovery.native" -kind = "model_file_listings" -counts = { example = 12, failure = 326, fixture = 12, seed = 50 } - -[[entry]] -engine = "native-preview" -stage = "lineage_facts" -site = "lineage_facts.native" -kind = "lineage_graphs" -counts = { example = 9, failure = 113, fixture = 6, seed = 22 } - -[[entry]] -engine = "native-preview" -stage = "macro_call_store" -site = "macro_call_store.native" -kind = "served_calls" -counts = { example = 18, fixture = 15, seed = 192 } - [[entry]] engine = "native-preview" stage = "macro_calls" @@ -440,20 +83,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "macro_calls" -site = "macro_calls.native" -kind = "bridged_calls" -counts = { example = 32, failure = 9, fixture = 28, seed = 719 } - -[[entry]] -engine = "native-preview" -stage = "macro_calls" -site = "macro_calls.native" -kind = "parsed_arguments" -counts = { example = 6, failure = 16, fixture = 5, seed = 369 } - [[entry]] engine = "native-preview" stage = "macro_calls" @@ -461,90 +90,6 @@ site = "macro_calls.resolution" kind = "deferred" counts = { failure = 4, seed = 3 } -[[entry]] -engine = "native-preview" -stage = "model_analysis" -site = "model_analysis.native" -kind = "dynamic_contract_proofs" -counts = { seed = 3 } - -[[entry]] -engine = "native-preview" -stage = "model_analysis" -site = "model_analysis.native" -kind = "expression_shapes" -counts = { example = 4, failure = 119, fixture = 4, seed = 100 } - -[[entry]] -engine = "native-preview" -stage = "model_analysis" -site = "model_analysis.native" -kind = "model_analyses" -counts = { example = 56, failure = 232, fixture = 52, seed = 698 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.native" -kind = "config_builds" -counts = { example = 56, failure = 347, fixture = 64, seed = 914 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.native" -kind = "effective_vars" -counts = { example = 29, failure = 273, fixture = 15, seed = 121 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.native" -kind = "header_metadata_parses" -counts = { example = 56, failure = 402, fixture = 60, seed = 1116 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.native" -kind = "template_expansions" -counts = { example = 214, failure = 1657, fixture = 333, seed = 1471 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.native" -kind = "validations" -counts = { example = 56, failure = 441, fixture = 64, seed = 1124 } - -[[entry]] -engine = "native-preview" -stage = "model_loop" -site = "model_loop.native" -kind = "cursor_intrinsic_validations" -counts = { example = 56, failure = 448, fixture = 64, seed = 1926 } - -[[entry]] -engine = "native-preview" -stage = "model_loop" -site = "model_loop.native" -kind = "declaration_contexts" -counts = { example = 64, failure = 136, fixture = 60, seed = 964 } - -[[entry]] -engine = "native-preview" -stage = "model_loop" -site = "model_loop.native" -kind = "declaration_expansions" -counts = { example = 56, failure = 468, fixture = 64, seed = 1127 } - -[[entry]] -engine = "native-preview" -stage = "model_loop" -site = "model_loop.native" -kind = "variable_substitutions" -counts = { example = 56, failure = 522, fixture = 72, seed = 1187 } - [[entry]] engine = "native-preview" stage = "project_assembly" @@ -552,27 +97,6 @@ site = "project_assembly.assembly" kind = "deferred" counts = { failure = 4 } -[[entry]] -engine = "native-preview" -stage = "project_assembly" -site = "project_assembly.native" -kind = "project_assemblies" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } - -[[entry]] -engine = "native-preview" -stage = "reference_extraction" -site = "reference_extraction.native" -kind = "reference_scans" -counts = { example = 250, failure = 512, fixture = 249, seed = 1532 } - -[[entry]] -engine = "native-preview" -stage = "semantic_checks" -site = "semantic_checks.native" -kind = "semantic_completions" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } - [[entry]] engine = "native-preview" stage = "sql_test_glue" @@ -580,20 +104,6 @@ site = "sql_test_glue.assembly" kind = "deferred" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "sql_test_glue" -site = "sql_test_glue.native" -kind = "sql_test_assemblies" -counts = { example = 16, failure = 7, fixture = 20, seed = 143 } - -[[entry]] -engine = "native-preview" -stage = "sql_test_glue" -site = "sql_test_glue.native" -kind = "sql_test_plans" -counts = { example = 1, failure = 5, fixture = 1, seed = 9 } - [[entry]] engine = "python" stage = "attachments" From 44fc323d6db2a7865943a611be2883eb591ef604 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:46:03 +0100 Subject: [PATCH 04/36] feat(native): analyse models in a ref cycle in the native session A ref cycle no longer hands the whole project to Python. Like Python's dataflow, the session analyses every model in one unordered wave over the shapes known up front and, like Python's completion pass on a CycleError, keeps those analyses without enrichment or binding re-validation. The cycle flag is part of the analysis-cache key. --- .../_helpers/analysis_cache.rs | 1 + .../analysis_session/_helpers/session.rs | 26 ++-- .../src/assembly/analysis_session/models.rs | 2 + .../tests/test_analysis_session.rs | 6 +- .../compiler/analysis_session/_test_types.py | 21 +++ .../compiler/analysis_session/helpers.py | 47 +++++++ .../test_native_model_analysis.py | 131 +++++++++++++++++- 7 files changed, 215 insertions(+), 19 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs index a58be1e7af..f725df3e1e 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs @@ -100,6 +100,7 @@ impl AnalysisSession { } } key.flag(self.dependency_ordered); + key.flag(self.cyclic); key.finish() } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index b9530e6ab9..f71f52182b 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -44,27 +44,31 @@ use crate::semantic_validation::types::DiagnosticRow; type Enrichments = (Vec, HashMap); impl AnalysisSession { - /// A session over `request`, or None where Python's cycle handling must analyse. + /// A session over `request`, or None where two models share a name; a `ref` cycle is one wave. pub(crate) fn start(request: SessionRequest, catalog: &ProjectCatalog) -> Option { - let waves: Vec> = waves(&producers(&request.models)?)?; + let scheduled: Option>> = waves(&producers(&request.models)?); let complete_shapes: ShapeTable = ShapeTable::from_shapes(&request.complete_schemas); - let dependency_ordered: bool = request - .models - .iter() - .flat_map(|model| model.required_names.iter()) - .any(|name| complete_shapes.get(name).is_none_or(Vec::is_empty)); + let dependency_ordered: bool = scheduled.is_some() + && request + .models + .iter() + .flat_map(|model| model.required_names.iter()) + .any(|name| complete_shapes.get(name).is_none_or(Vec::is_empty)); + let cyclic: bool = scheduled.is_none(); + let waves: Vec> = scheduled.unwrap_or_default(); Some(Self { catalog: SessionCatalog::new(catalog, &request.catalog_schemas), available_types: ShapeTable::from_shapes(&request.column_types), available_nullability: ShapeTable::from_shapes(&request.column_nullability), complete_shapes, outcomes: vec![None; request.models.len()], - waves: if dependency_ordered || waves.is_empty() { + waves: if dependency_ordered || request.models.is_empty() { waves } else { vec![(0..request.models.len()).collect()] }, dependency_ordered, + cyclic, next_wave: 0, phase: Phase::Analyze, publications: Vec::new(), @@ -355,6 +359,9 @@ impl AnalysisSession { /// Python's `_complete_inferred_bindings` star and type pass for one wave. fn complete_wave(&mut self, wave: usize) -> Result { let mut deferrals: Vec = Vec::new(); + if self.cyclic { + return Ok((deferrals, HashMap::new())); + } let mut star_pending_by_model: HashMap = HashMap::new(); let mut candidates: Vec<(usize, Shapes, bool)> = Vec::new(); for model in self.waves[wave].clone() { @@ -460,7 +467,8 @@ impl AnalysisSession { fn finish_wave(&mut self, wave: usize) -> Result<(), String> { let models: Vec = self.waves[wave].clone(); let mut validations: Vec<(usize, String, Shapes)> = Vec::new(); - for model in &models { + let validated: &[usize] = if self.cyclic { &[] } else { &models }; + for model in validated { let schema: Shapes = self.binding_schema(&self.request.models[*model]); let outcome: &ModelOutcome = self.outcomes[*model] .as_ref() diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 4e020bdf17..f8cdd3b737 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -304,6 +304,8 @@ pub struct AnalysisSession { pub(crate) request: SessionRequest, pub(crate) catalog: SessionCatalog, pub(crate) dependency_ordered: bool, + /// Whether models `ref` each other in a cycle, so Python's completion pass is skipped. + pub(crate) cyclic: bool, pub(crate) waves: Vec>, pub(crate) next_wave: usize, pub(crate) available_types: ShapeTable, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 50f4d2dab9..9e9b9bd6db 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -242,10 +242,10 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe } #[test] -fn given_unschedulable_models_when_starting_then_python_analyses() { +fn given_models_when_starting_then_only_duplicate_names_leave_analysis_to_python() { let test_cases = [ UnscheduledTestCase { - description: "models that reference each other", + description: "models that reference each other analyse in one unordered wave", models: &[ ( "orders", @@ -260,7 +260,7 @@ fn given_unschedulable_models_when_starting_then_python_analyses() { &["orders"], ), ], - expected_started: false, + expected_started: true, }, UnscheduledTestCase { description: "two models with one name", diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 6bce16f42e..f6cc2c9610 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -39,6 +39,27 @@ class AnalysisFallbackTestCase: expected_kind: str +@dataclass(frozen=True) +class CyclicAnalysisTestCase: + """Models that `ref` each other, which the native session analyses in one unordered wave.""" + + description: str + files: dict[str, str] + dialects: tuple[str, ...] + lineage_modes: tuple[ColumnLineageMode, ...] + expected_analysed: int + + +@dataclass(frozen=True) +class CyclicCompileTestCase: + """A project whose models `ref` each other, compiled through the CLI.""" + + description: str + files: dict[str, str] + expected_exit_code: int + expected_compiled: tuple[str, ...] + + @dataclass(frozen=True) class SessionFailureTestCase: """A session that fails after Python answered deferrals, so Python analyses everything.""" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 2f11c54cef..0b4f769781 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -20,6 +20,7 @@ import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stage_assembly from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter +from sqlbuild.cli.entry.main.entry import main from sqlbuild.compiler.analysis_session.main._analyze_native_model_sql import ( analyze_native_model_sql, ) @@ -529,6 +530,52 @@ def compile_inputs(*, project_dir: Path, files: dict[str, str]) -> CompileProjec ) +def compiled_project_view( + *, + project_dir: Path, + files: dict[str, str], + capsys: pytest.CaptureFixture[str], +) -> tuple[int, object, object, dict[str, str]]: + """Compile a written project through the CLI: exit code, report, manifest nodes, SQL files.""" + + for relative_path, contents in files.items(): + path: Path = project_dir / relative_path + path.parent.mkdir(parents=True, exist_ok=True) + _ = path.write_text(contents, encoding="utf-8") + _ = capsys.readouterr() + code: int = main(["--project-dir", str(project_dir), "compile", "--json", "--manifest"]) + report: dict[str, object] = json.loads(capsys.readouterr().out) + manifest: dict[str, object] = json.loads( + (project_dir / "target" / "manifest.json").read_text(encoding="utf-8") + ) + compiled: Path = project_dir / "target" / "compiled" + _ = report.pop("compile_timings") + _ = manifest.pop("metadata") + return ( + code, + report, + manifest, + { + path.relative_to(compiled).as_posix(): path.read_text(encoding="utf-8") + for path in sorted(compiled.rglob("*.sql")) + }, + ) + + +def python_compiled_project_view( + *, + project_dir: Path, + files: dict[str, str], + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> tuple[int, object, object, dict[str, str]]: + """`compiled_project_view` with native model analysis deferred, so Python analyses all.""" + + with monkeypatch.context() as patch: + patch.setattr(native_stage_assembly, "analyze_native_model_sql", lambda **_kwargs: None) + return compiled_project_view(project_dir=project_dir, files=files, capsys=capsys) + + @dataclass class AnalysisParity: """Python's and the native session's views of every analysis seam call.""" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index ce17a2bb06..518e9f3fc7 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -5,6 +5,7 @@ import random from collections import Counter from dataclasses import replace +from itertools import product from pathlib import Path from typing import Any @@ -28,6 +29,8 @@ from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( AnalysisFallbackTestCase, + CyclicAnalysisTestCase, + CyclicCompileTestCase, GeneratedAnalysisParityTestCase, SessionFailureTestCase, SharedAnalysisTestCase, @@ -41,12 +44,14 @@ analysis_request, compare_analyses, compile_inputs, + compiled_project_view, custom_nullability_rule, deferral_kinds, failing_provide_sessions, generated_analysis_files, native_pivot_proofs, pivot_project_files, + python_compiled_project_view, shared_analysis_files, started_sessions, ) @@ -60,6 +65,30 @@ SnowflakeAdapter().expression_inference_profile(), DuckDbAdapter().expression_inference_profile(), ) +_CYCLIC_PROJECT: dict[str, str] = { + "sqlbuild_project.toml": 'name = "orders_cycle"\nadapter = "duckdb"\n', + "sources/raw.yml": ( + "sources:\n - name: raw_orders\n description: Raw orders.\n columns:\n" + " - name: order_id\n type: INTEGER\n" + " - name: amount\n type: DOUBLE\n" + ), + "models/orders.sql": ( + 'MODEL (description "Orders");\n\nSELECT o.order_id, o.amount, r.refund\n' + 'FROM __source("raw_orders") AS o\n' + 'LEFT JOIN __ref("returns") AS r ON r.order_id = o.order_id\n' + ), + "models/returns.sql": ( + 'MODEL (description "Returns");\n\n' + 'SELECT order_id, amount * -1 AS refund FROM __ref("orders")\n' + ), + "models/order_summary.sql": ( + 'MODEL (description "Summary");\n\nSELECT * FROM __ref("returns")\n' + ), + "models/order_audit.sql": ( + 'MODEL (description "Audit");\n\n' + 'SELECT order_id, missing_column FROM __ref("order_audit")\n' + ), +} _ORDERS_PROJECT: dict[str, str] = { "sqlbuild_project.toml": 'name = "orders_cycle"\nadapter = "duckdb"\n', "models/orders.sql": 'MODEL (description "Orders");\n\nSELECT order_id FROM __ref("returns")\n', @@ -264,13 +293,101 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match @pytest.mark.parametrize( "test_case", [ - AnalysisFallbackTestCase( - description="models that reference each other", - files=_ORDERS_PROJECT, - allow_compact_analysis=True, - keeps_catalog=True, - expected_kind="session", - ), + CyclicAnalysisTestCase( + description="a ref cycle, a model reading itself and a star consumer of the cycle", + files=_CYCLIC_PROJECT, + dialects=("duckdb", "snowflake"), + lineage_modes=(ColumnLineageMode.FAST, ColumnLineageMode.RICH), + expected_analysed=16, + ) + ], + ids=lambda case: case.description, +) +def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_matches_python( + test_case: CyclicAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + inputs: CompileProjectInputs = compile_inputs( + project_dir=tmp_path / "project", files=test_case.files + ) + sessions: list[Any] = started_sessions(monkeypatch=monkeypatch) + parity: AnalysisParity = AnalysisParity() + + _ = [ + compare_analyses( + inputs=inputs, + inference_profile=ExpressionInferenceProfile(sql_analysis_dialect=dialect), + lineage_mode=lineage_mode, + parity=parity, + monkeypatch=monkeypatch, + ) + for dialect, lineage_mode in product(test_case.dialects, test_case.lineage_modes) + ] + + assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] + assert ( + parity.analysed_models, + len(sessions), + None in sessions, + deferral_kinds(record_dir), + ) == ( + test_case.expected_analysed, + len(test_case.dialects) * len(test_case.lineage_modes), + False, + Counter(), + ) + + +@pytest.mark.parametrize( + "test_case", + [ + CyclicCompileTestCase( + description="a ref cycle, a model reading itself and a star consumer of the cycle", + files=_CYCLIC_PROJECT, + expected_exit_code=0, + expected_compiled=( + "models/order_audit.sql", + "models/order_summary.sql", + "models/orders.sql", + "models/returns.sql", + ), + ) + ], + ids=lambda case: case.description, +) +def test_given_cyclic_project_when_compiling_then_native_output_equals_python_analysis( + test_case: CyclicCompileTestCase, + tmp_path: Path, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE.value) + native: tuple[int, object, object, dict[str, str]] = compiled_project_view( + project_dir=tmp_path / "native" / "orders_cycle", files=test_case.files, capsys=capsys + ) + native_deferrals: Counter[str] = deferral_kinds(record_dir) + + python: tuple[int, object, object, dict[str, str]] = python_compiled_project_view( + project_dir=tmp_path / "python" / "orders_cycle", + files=test_case.files, + capsys=capsys, + monkeypatch=monkeypatch, + ) + + assert native == python + assert (native[0], tuple(native[3]), native_deferrals) == ( + test_case.expected_exit_code, + test_case.expected_compiled, + Counter(), + ) + + +@pytest.mark.parametrize( + "test_case", + [ AnalysisFallbackTestCase( description="lineage disabled, so compact analysis is off", files=_ORDERS_PROJECT, From 228f6824a9338377182a0806902a3ee53fb5caaa Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:11:22 +0100 Subject: [PATCH 05/36] feat(native)!: normalize every type and contract natively with Python's Unicode semantics Generated casefold/upper tables per Unicode version, the running Python's text semantics recorded at import, all Polyglot dialects compiled in, arbitrary-size type parameters, stack growth for deep types, and Python's errors for unknown dialects. Deletes the Python type normalization and per-model contract fallbacks. --- Cargo.lock | 1 + Cargo.toml | 7 +- crates/sqlbuild-analysis/Cargo.toml | 1 + .../analysis_session/_helpers/cte_facts.rs | 2 +- .../src/contracts/_helpers/columns.rs | 7 +- .../src/contracts/_helpers/dynamic.rs | 24 +- .../src/contracts/_helpers/type_comparison.rs | 22 +- .../main/evaluate_model_contracts.rs | 23 +- .../sqlbuild-analysis/src/contracts/models.rs | 38 - .../src/contracts/tests/helpers.rs | 10 +- .../tests/test_evaluate_model_contracts.rs | 57 +- .../src/lineage/main/parser_dialect.rs | 19 +- .../_helpers/metadata_checks/families.rs | 4 +- .../_helpers/sql_text/parsed_sql.rs | 22 +- .../src/type_system/_helpers/dialects.rs | 22 +- .../src/type_system/_helpers/fallback.rs | 68 +- .../src/type_system/_helpers/parsed.rs | 185 +- .../src/type_system/_helpers/python_text.rs | 107 +- .../src/type_system/constants.rs | 8 +- .../src/type_system/main/normalize_type.rs | 53 +- .../src/type_system/models.rs | 70 +- .../src/type_system/tests/helpers.rs | 11 +- .../type_system/tests/test_normalize_type.rs | 71 +- .../src/type_system/tests/test_python_text.rs | 60 +- .../src/type_system/tests/test_types.rs | 10 +- .../src/text/_helpers/case_tables.rs | 28 + crates/sqlbuild-core/src/text/_helpers/mod.rs | 1 + .../text/casefold_mappings/unicode_15_0_0.in | 1534 ++++++++++++++++ .../text/casefold_mappings/unicode_15_1_0.in | 1534 ++++++++++++++++ .../text/casefold_mappings/unicode_16_0_0.in | 1561 +++++++++++++++++ .../src/text/main/active_python_text.rs | 27 + crates/sqlbuild-core/src/text/main/mod.rs | 4 + .../src/text/main/python_casefold.rs | 12 + .../src/text/main/python_decimal_value.rs | 21 + .../src/text/main/python_text.rs | 11 + .../src/text/main/python_upper.rs | 12 + crates/sqlbuild-core/src/text/models.rs | 4 + .../src/text/tests/test_python_compat.rs | 49 +- .../src/text/tests/test_types.rs | 8 + .../src/text/upper_mappings/unicode_15_0_0.in | 1529 ++++++++++++++++ .../src/text/upper_mappings/unicode_15_1_0.in | 1529 ++++++++++++++++ .../src/text/upper_mappings/unicode_16_0_0.in | 1556 ++++++++++++++++ .../src/bindings/_helpers/boundary/mod.rs | 1 + .../_helpers/boundary/python_runtime.rs | 22 + .../_helpers/contracts/model_contracts.rs | 27 +- .../_helpers/type_system/normalization.rs | 60 +- .../src/bindings/main/register.rs | 1 + scripts/python_char_table/_helpers/table.py | 44 +- scripts/python_char_table/constants.py | 2 + scripts/python_char_table/main/generate.py | 19 +- src/sqlbuild/_native.pyi | 27 +- .../_helpers/type_normalization.py | 365 +--- .../main/_native_normalize_type.py | 27 +- .../compiler/contracts/_helpers/columns.py | 421 ----- .../compiler/contracts/_helpers/evaluation.py | 42 - .../contracts/_helpers/native_contracts.py | 39 +- .../contracts/_helpers/native_deferrals.py | 23 - .../contracts/_helpers/output_locations.py | 51 + src/sqlbuild/compiler/contracts/constants.py | 1 - src/sqlbuild/compiler/frontier/types.py | 1 - .../adapter/type_system/_test_types.py | 34 +- .../sqlbuild/adapter/type_system/helpers.py | 73 +- .../test_native_type_normalization.py | 310 ++++ .../type_system/test_native_type_parity.py | 238 --- .../compiler/contracts/_test_types.py | 18 +- .../sqlbuild/compiler/contracts/helpers.py | 100 +- .../contracts/test_native_model_contracts.py | 100 +- .../type_system/test_type_normalization.py | 134 -- 68 files changed, 10542 insertions(+), 1960 deletions(-) create mode 100644 crates/sqlbuild-core/src/text/_helpers/case_tables.rs create mode 100644 crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_0_0.in create mode 100644 crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_1_0.in create mode 100644 crates/sqlbuild-core/src/text/casefold_mappings/unicode_16_0_0.in create mode 100644 crates/sqlbuild-core/src/text/main/active_python_text.rs create mode 100644 crates/sqlbuild-core/src/text/main/python_casefold.rs create mode 100644 crates/sqlbuild-core/src/text/main/python_decimal_value.rs create mode 100644 crates/sqlbuild-core/src/text/main/python_upper.rs create mode 100644 crates/sqlbuild-core/src/text/upper_mappings/unicode_15_0_0.in create mode 100644 crates/sqlbuild-core/src/text/upper_mappings/unicode_15_1_0.in create mode 100644 crates/sqlbuild-core/src/text/upper_mappings/unicode_16_0_0.in create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs delete mode 100644 src/sqlbuild/compiler/contracts/_helpers/columns.py delete mode 100644 src/sqlbuild/compiler/contracts/_helpers/evaluation.py delete mode 100644 src/sqlbuild/compiler/contracts/_helpers/native_deferrals.py create mode 100644 src/sqlbuild/compiler/contracts/_helpers/output_locations.py create mode 100644 tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py delete mode 100644 tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py diff --git a/Cargo.lock b/Cargo.lock index 0d533db2ee..c4a8cedcaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,6 +707,7 @@ dependencies = [ "sqlbuild-model-config", "sqlbuild-scopes", "sqlbuild-sqltext", + "stacker", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cb2519d85b..75f7a092a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,12 +55,7 @@ pyo3 = { version = "0.26", features = ["abi3-py312"] } polyglot-sql = { git = "https://github.com/chio-labs/polyglot.git", rev = "ca9e7b6e405fb6f5484472bb08780268789c5e04", default-features = false, features = [ "ast-tools", "builder", - "dialect-bigquery", - "dialect-databricks", - "dialect-duckdb", - "dialect-postgresql", - "dialect-snowflake", - "dialect-tsql", + "all-dialects", "semantic", "stacker", ] } diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 4a1d764352..6b963edcb8 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -25,6 +25,7 @@ sqlbuild-core.workspace = true sqlbuild-model-config.workspace = true sqlbuild-scopes.workspace = true sqlbuild-sqltext.workspace = true +stacker.workspace = true [lints] workspace = true diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs index 83ec0e1a0e..e945f3e3b4 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs @@ -665,7 +665,7 @@ impl Context<'_, '_> { fn normalized(&self, type_sql: &str) -> Fact { normalize_type(type_sql, self.profile.dialect) .map(|normalization| normalization.normalized) - .ok_or_else(|| format!("type normalization of {type_sql} defers")) + .map_err(|error| error.message()) } /// Python's `_polyglot_direct_select_output_types`. diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs index 20922fe52b..469815df59 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs @@ -13,9 +13,10 @@ use crate::contracts::constants::{ UNPROVEN_TYPE_OUTPUT_MESSAGE, }; use crate::contracts::models::{ - ContractDeferral, ContractDiagnostic, ContractLocation, ContractModel, ContractSchema, + ContractDiagnostic, ContractLocation, ContractModel, ContractSchema, ContractSeverity, DeclaredColumn, InferredOutputColumn, RelatedOutput, }; +use crate::type_system::models::TypeNormalizationError; /// Python's `_requires_contract_evaluation`. pub(crate) fn requires_contract_evaluation(model: &ContractModel, implicit: bool) -> bool { @@ -46,7 +47,7 @@ pub(crate) fn model_contract_diagnostics( model: &ContractModel, validate_declared_shape: bool, types: &TypeComparer<'_>, -) -> Result, ContractDeferral> { +) -> Result, TypeNormalizationError> { let contract_enforced: bool = model.contract.as_deref() == Some(CONTRACT_ENFORCED); let Some(schema) = model .schema @@ -249,7 +250,7 @@ fn missing_column( fn column_type( column: &TypedColumn<'_>, check: &TypeCheck<'_>, -) -> Result, ContractDeferral> { +) -> Result, TypeNormalizationError> { let name: &str = &column.declared.name; let declared_type: &str = column.declared_type; let Some(inferred_type) = column.inferred.inferred_type.as_deref() else { diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs index 058b485381..d48ac54779 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs @@ -2,6 +2,9 @@ use std::collections::HashMap; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; + use crate::contracts::_helpers::type_comparison::{TypeComparer, TypeComparison}; use crate::contracts::constants::{ DYNAMIC_FAMILY_TYPE_MISMATCH_HELP, DYNAMIC_FAMILY_UNKNOWN_TYPE_HELP, @@ -9,15 +12,16 @@ use crate::contracts::constants::{ TYPE_MISMATCH_CODE, UNKNOWN_TYPE_CODE, }; use crate::contracts::models::{ - ContractDeferral, ContractDiagnostic, ContractLocation, ContractModel, ContractSeverity, + ContractDiagnostic, ContractLocation, ContractModel, ContractSeverity, DynamicContractProof, }; +use crate::type_system::models::TypeNormalizationError; /// The dynamic column contract diagnostics of one model, before its fixed columns. pub(crate) fn dynamic_column_diagnostics( model: &ContractModel, types: &TypeComparer<'_>, -) -> Result, ContractDeferral> { +) -> Result, TypeNormalizationError> { let Some(schema) = model .schema .as_ref() @@ -35,7 +39,7 @@ pub(crate) fn dynamic_column_diagnostics( let proof_by_name: HashMap> = families_by_casefold(proof)?; let mut diagnostics: Vec = Vec::new(); for family in &schema.dynamic_columns { - let key: String = casefold(&family.name)?; + let key: String = casefold(&family.name); let declared: &str = &family.declared_type; let Some(inferred) = proof_by_name.get(&key).copied().flatten() else { diagnostics.push(family_diagnostic( @@ -87,21 +91,17 @@ fn not_proven(model: &ContractModel) -> ContractDiagnostic { fn families_by_casefold( proof: &DynamicContractProof, -) -> Result>, ContractDeferral> { +) -> Result>, TypeNormalizationError> { let mut by_name: HashMap> = HashMap::new(); for (name, inferred_type) in &proof.families { - let _ = by_name.insert(casefold(name)?, inferred_type.as_deref()); + let _ = by_name.insert(casefold(name), inferred_type.as_deref()); } Ok(by_name) } -/// Python's `str.casefold` for ASCII names; other names defer. -fn casefold(name: &str) -> Result { - if name.is_ascii() { - Ok(name.to_ascii_lowercase()) - } else { - Err(ContractDeferral::NonAsciiFamilyName) - } +/// Python's `str.casefold`. +fn casefold(name: &str) -> String { + python_casefold(active_python_text(), name) } fn family_diagnostic( diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs index 13bb94ae69..76c2ca77d4 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs @@ -1,11 +1,10 @@ -//! Python's `types_equal` over the native type system, deferring where it cannot answer. +//! Python's `types_equal` over the native type system. use std::cell::RefCell; use std::collections::HashMap; -use crate::contracts::models::ContractDeferral; use crate::type_system::main::normalize_type::normalize_type; -use crate::type_system::models::{NormalizedType, TypeNormalization}; +use crate::type_system::models::{NormalizedType, TypeNormalization, TypeNormalizationError}; /// Whether two type strings normalize to the same comparison shape. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -17,7 +16,7 @@ pub(crate) enum TypeComparison { /// One request's type comparisons, normalizing each type string once as Python's cache does. pub(crate) struct TypeComparer<'a> { dialect: &'a str, - normalized: RefCell>>, + normalized: RefCell>>, } impl<'a> TypeComparer<'a> { @@ -28,12 +27,12 @@ impl<'a> TypeComparer<'a> { } } - /// Compare two types as Python's `types_equal`, or defer when either has no native answer. + /// Compare two types as Python's `types_equal`, or Python's normalization error. pub(crate) fn types_equal( &self, left: &str, right: &str, - ) -> Result { + ) -> Result { let left: NormalizedType = self.normalized(left)?; let right: NormalizedType = self.normalized(right)?; Ok(if left == right { @@ -43,15 +42,16 @@ impl<'a> TypeComparer<'a> { }) } - fn normalized(&self, type_sql: &str) -> Result { + fn normalized(&self, type_sql: &str) -> Result { if let Some(known) = self.normalized.borrow().get(type_sql) { - return known.clone().ok_or(ContractDeferral::TypeNormalization); + return known.clone(); } - let normalized: Option = normalize_type(type_sql, self.dialect) - .map(|TypeNormalization { normalized, .. }| normalized); + let normalized: Result = + normalize_type(type_sql, self.dialect) + .map(|TypeNormalization { normalized, .. }| normalized); self.normalized .borrow_mut() .insert(type_sql.to_owned(), normalized.clone()); - normalized.ok_or(ContractDeferral::TypeNormalization) + normalized } } diff --git a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs index a1fa939fbf..db0a25dfbc 100644 --- a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs @@ -4,11 +4,17 @@ use crate::contracts::_helpers::columns::{ declared_shape_validation_is_active, model_contract_diagnostics, requires_contract_evaluation, }; use crate::contracts::_helpers::type_comparison::TypeComparer; -use crate::contracts::models::{ContractOutcome, ContractRequest}; +use crate::contracts::models::{ContractDiagnostic, ContractRequest}; +use crate::type_system::models::TypeNormalizationError; -/// One outcome per requested model; models that need no evaluation have no diagnostics. -#[must_use] -pub fn evaluate_model_contracts(request: &ContractRequest) -> Vec { +/// Each requested model's diagnostics; models that need no evaluation have none. +/// +/// # Errors +/// +/// The type normalization error Python raises while comparing a model's types. +pub fn evaluate_model_contracts( + request: &ContractRequest, +) -> Result>, TypeNormalizationError> { let implicit: bool = request.implicit_column_contracts; let types = TypeComparer::new(&request.dialect); request @@ -16,16 +22,13 @@ pub fn evaluate_model_contracts(request: &ContractRequest) -> Vec ContractOutcome::Diagnostics(diagnostics), - Err(deferral) => ContractOutcome::Deferred(deferral), - } + ) }) .collect() } diff --git a/crates/sqlbuild-analysis/src/contracts/models.rs b/crates/sqlbuild-analysis/src/contracts/models.rs index 131649c921..656f80975a 100644 --- a/crates/sqlbuild-analysis/src/contracts/models.rs +++ b/crates/sqlbuild-analysis/src/contracts/models.rs @@ -130,44 +130,6 @@ pub struct ContractDiagnostic { pub help: String, } -/// Why native contract validation hands one model back to Python. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ContractDeferral { - /// Native type normalization cannot reproduce Python for a compared type. - TypeNormalization, - /// A dynamic family name outside ASCII, whose `casefold` native does not reproduce. - NonAsciiFamilyName, -} - -impl ContractDeferral { - /// The deferral kind recorded for the harness. - #[must_use] - pub fn as_str(self) -> &'static str { - match self { - Self::TypeNormalization => "type_normalization", - Self::NonAsciiFamilyName => "non_ascii_family_name", - } - } -} - -/// One model's contract outcome. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ContractOutcome { - Diagnostics(Vec), - Deferred(ContractDeferral), -} - -impl ContractOutcome { - /// `(deferral, diagnostics)`: a deferred model has no diagnostics. - #[must_use] - pub fn into_parts(self) -> (Option, Vec) { - match self { - Self::Diagnostics(diagnostics) => (None, diagnostics), - Self::Deferred(deferral) => (Some(deferral), Vec::new()), - } - } -} - /// The inputs to table promotion conflict detection. #[derive(Debug, Clone, PartialEq, Eq)] pub struct PromotionRequest { diff --git a/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs b/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs index 542f3a7d64..c18efef848 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs @@ -1,5 +1,5 @@ use crate::contracts::models::{ - ContractDiagnostic, ContractModel, ContractOutcome, ContractSchema, DeclaredColumn, + ContractDiagnostic, ContractModel, ContractSchema, DeclaredColumn, DeclaredColumnFamily, DynamicContractProof, InferredOutputColumn, PromotionModel, PromotionRequest, }; @@ -99,12 +99,8 @@ pub(crate) fn promotion_model( } } -pub(crate) fn outcome_lines(outcome: &ContractOutcome) -> Vec { - let (deferral, diagnostics) = outcome.clone().into_parts(); - deferral.map_or_else( - || diagnostics.iter().map(diagnostic_line).collect(), - |deferral| vec![format!("deferred:{}", deferral.as_str())], - ) +pub(crate) fn outcome_lines(diagnostics: &[ContractDiagnostic]) -> Vec { + diagnostics.iter().map(diagnostic_line).collect() } fn diagnostic_line(diagnostic: &ContractDiagnostic) -> String { diff --git a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs index 2b3883d3e1..09496ff85c 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs @@ -1,5 +1,5 @@ use crate::contracts::main::evaluate_model_contracts::evaluate_model_contracts; -use crate::contracts::models::{ContractModel, ContractOutcome, ContractRequest}; +use crate::contracts::models::{ContractDiagnostic, ContractModel, ContractRequest}; use crate::contracts::tests::helpers::{ declared, dynamic_model, inferred, model, outcome_lines, proof, schema, with_inferred, }; @@ -132,7 +132,7 @@ fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() expected_lines: &[], }, ContractTestCase { - description: "a type native normalization cannot answer defers the model", + description: "a non-ASCII type upper-cases with Python's Unicode mapping", implicit: true, model: with_inferred( model( @@ -141,7 +141,10 @@ fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() ), vec![inferred("amount", Some("VARCHAR"), false)], ), - expected_lines: &["deferred:type_normalization"], + expected_lines: &[ + "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is \ + T\u{c9}XT [amount: inferred VARCHAR]", + ], }, ContractTestCase { description: "an unproven dynamic contract without a reason", @@ -186,13 +189,13 @@ fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() ], }, ContractTestCase { - description: "a family name outside ASCII defers the model", + description: "a family name outside ASCII matches under Python's casefold", implicit: true, model: dynamic_model( &[("straße_*", "DOUBLE")], Some(proof(true, None, &[("strasse_*", Some("DOUBLE"))])), ), - expected_lines: &["deferred:non_ascii_family_name"], + expected_lines: &[], }, ]; for test_case in test_cases { @@ -201,9 +204,13 @@ fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() implicit_column_contracts: test_case.implicit, models: vec![test_case.model], }; - let outcomes: Vec = evaluate_model_contracts(&request); + let outcomes: Vec> = + evaluate_model_contracts(&request).expect("duckdb types normalize"); assert_eq!( - outcomes.iter().flat_map(outcome_lines).collect::>(), + outcomes + .iter() + .flat_map(|diagnostics| outcome_lines(diagnostics)) + .collect::>(), test_case.expected_lines, "{}", test_case.description @@ -226,7 +233,7 @@ fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own ) }; let test_cases = [SharedTypesTestCase { - description: "repeated equal, mismatched and unanswerable types", + description: "repeated equal, mismatched and non-ASCII types", models: vec![ enforced("INT", "INTEGER"), enforced("INT", "VARCHAR"), @@ -237,9 +244,9 @@ fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own ], expected_lines: &[ "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is INT [amount: inferred VARCHAR]", - "deferred:type_normalization", + "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is T\u{c9}XT [amount: inferred VARCHAR]", "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is INT [amount: inferred VARCHAR]", - "deferred:type_normalization", + "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is T\u{c9}XT [amount: inferred VARCHAR]", ], }]; for test_case in test_cases { @@ -249,14 +256,18 @@ fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own models, }; let together: Vec = evaluate_model_contracts(&request(test_case.models.clone())) + .expect("duckdb types normalize") .iter() - .flat_map(outcome_lines) + .flat_map(|diagnostics| outcome_lines(diagnostics)) .collect(); let alone: Vec = test_case .models .iter() - .flat_map(|model| evaluate_model_contracts(&request(vec![model.clone()]))) - .flat_map(|outcome| outcome_lines(&outcome)) + .flat_map(|model| { + evaluate_model_contracts(&request(vec![model.clone()])) + .expect("duckdb types normalize") + }) + .flat_map(|diagnostics| outcome_lines(&diagnostics)) .collect(); assert_eq!(together, alone, "{}", test_case.description); @@ -267,3 +278,23 @@ fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own ); } } + +#[test] +fn given_a_dialect_polyglot_does_not_know_when_comparing_types_then_python_error_is_returned() { + let request = ContractRequest { + dialect: "motherduck".to_owned(), + implicit_column_contracts: true, + models: vec![with_inferred( + model( + Some("enforced"), + Some(schema(vec![declared("amount", Some("INT"), false)], false)), + ), + vec![inferred("amount", Some("INTEGER"), false)], + )], + }; + + assert_eq!( + evaluate_model_contracts(&request).map_err(|error| error.message()), + Err("Unknown dialect: motherduck".to_owned()) + ); +} diff --git a/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs b/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs index 61a7005ee7..2c88b27913 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs @@ -1,22 +1,9 @@ -//! The polyglot dialect Python's `dialect or "generic"` names, if this parser build carries it. +//! The polyglot dialect Python's `dialect or "generic"` names, if Polyglot knows the name. use polyglot_sql::DialectType; -/// Python's `parse_one(dialect=dialect or "generic")` dialect, if this parser build carries it. +/// Python's `parse_one(dialect=dialect or "generic")` dialect, if Polyglot knows the name. pub fn parser_dialect(name: Option<&str>) -> Option { let name = name.filter(|name| !name.is_empty()).unwrap_or("generic"); - let Ok(dialect) = name.parse::() else { - return None; - }; - matches!( - dialect, - DialectType::Generic - | DialectType::PostgreSQL - | DialectType::BigQuery - | DialectType::Snowflake - | DialectType::DuckDB - | DialectType::TSQL - | DialectType::Databricks - ) - .then_some(dialect) + name.parse::().ok() } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs index c63fa668ec..b005765755 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs @@ -31,8 +31,8 @@ impl Families { /// The family of `type_sql`, or a deferral where Python would leave the native type system. pub(crate) fn family(&self, type_sql: &str) -> Result { - let normalization: TypeNormalization = - normalize_type(type_sql, &self.dialect).ok_or(SemanticDeferral::UnsupportedType)?; + let normalization: TypeNormalization = normalize_type(type_sql, &self.dialect) + .map_err(|_| SemanticDeferral::UnsupportedType)?; if normalization.parse_error.is_some() { self.fallback_types.borrow_mut().push(type_sql.to_owned()); } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs index d70a47139a..63240fc79b 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs @@ -15,25 +15,11 @@ use crate::semantic_checks::models::SemanticDeferral; use crate::semantic_validation::main::normalize::normalize_analysis_sql; use crate::semantic_validation::models::NormalizationInput; -/// The Polyglot dialect the wheel resolves `name` to, when this build includes it. +/// The Polyglot dialect the wheel resolves `name` to, when Polyglot knows the name. pub(crate) fn polyglot_dialect(name: &str) -> Result { - let Ok(dialect_type) = name.parse::() else { - return Err(SemanticDeferral::UnsupportedDialect); - }; - if matches!( - dialect_type, - DialectType::Generic - | DialectType::PostgreSQL - | DialectType::BigQuery - | DialectType::Snowflake - | DialectType::DuckDB - | DialectType::TSQL - | DialectType::Databricks - ) { - Ok(Dialect::get(dialect_type)) - } else { - Err(SemanticDeferral::UnsupportedDialect) - } + name.parse::() + .map(Dialect::get) + .map_err(|_| SemanticDeferral::UnsupportedDialect) } /// Python's `get_complete_schema_binding_request(...).sql` without placeholders. diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/dialects.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/dialects.rs index 6cabfabfeb..dc90fe4dd8 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/dialects.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/dialects.rs @@ -1,27 +1,7 @@ -//! Dialect names: Polyglot's resolution and Python's `TypeDialect` coercion. - -use polyglot_sql::{Dialect, DialectType}; +//! Dialect names: Python's `TypeDialect` coercion. use crate::type_system::models::TypeDialect; -/// The Polyglot dialect Python's wheel resolves `name` to, when this build includes it. -pub(crate) fn polyglot_dialect(name: &str) -> Option { - let Ok(dialect_type) = name.parse::() else { - return None; - }; - matches!( - dialect_type, - DialectType::Generic - | DialectType::PostgreSQL - | DialectType::BigQuery - | DialectType::Snowflake - | DialectType::DuckDB - | DialectType::TSQL - | DialectType::Databricks - ) - .then(|| Dialect::get(dialect_type)) -} - /// Python's `_coerce_type_dialect`: an exact `TypeDialect` value, otherwise None. pub(crate) fn coerce_type_dialect(name: &str) -> Option { Some(match name { diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs index 53d2ee1d28..fb7a431b68 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs @@ -1,7 +1,9 @@ //! Python's `_normalize_with_fallback`: type text normalized without a parser. +use sqlbuild_core::text::main::python_strip::python_strip; + use crate::type_system::_helpers::python_text::{ - python_strip, remove_python_space, split_type_and_params, + remove_python_space, split_type_and_params, upper, }; use crate::type_system::constants::{ BIGNUMERIC_TYPE_NAME, BOOLEAN_TYPE_NAMES, DATE_TYPE_NAME, DATETIME_TYPE_NAME, @@ -10,32 +12,32 @@ use crate::type_system::constants::{ NORMALIZED_TZ_INPUT_TYPE_NAME, STRING_TYPE_NAMES, TEXT_TYPE_NAME, TIMESTAMP_TYPE_TOKEN, UNBOUNDED_TEXT_TYPE_NAMES, }; -use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; +use crate::type_system::models::{NormalizedType, PythonInteger, TypeDialect, TypeFamily}; -/// Normalize ASCII type text as Python's fallback does; None for integers beyond `i64`. +/// Normalize type text as Python's `_normalize_with_fallback` does. pub(crate) fn normalize_with_fallback( type_sql: &str, dialect: Option, -) -> Option { - let normalized: String = remove_python_space(python_strip(&type_sql.to_ascii_uppercase())); - let (base_type, params) = split_type_and_params(&normalized)?; - let first: Option = params.first().copied(); - let second: Option = params.get(1).copied(); +) -> NormalizedType { + let normalized: String = remove_python_space(python_strip(&upper(type_sql))); + let (base_type, params) = split_type_and_params(&normalized); + let first: Option = params.first().cloned(); + let second: Option = params.get(1).cloned(); let snowflake: bool = dialect == Some(TypeDialect::Snowflake); let bigquery: bool = dialect == Some(TypeDialect::BigQuery); if INTEGER_TYPE_NAMES.contains(&base_type) { if snowflake { - return Some(snowflake_integer()); + return snowflake_integer(); } let name: &str = if bigquery { "INT64" } else { base_type }; - return Some(simple(name, TypeFamily::Integer)); + return simple(name, TypeFamily::Integer); } if DECIMAL_TYPE_NAMES.contains(&base_type) { let (mut precision, mut scale) = (first, second); if snowflake && precision.is_none() { - precision = Some(INTEGER_PRECISION); - scale = Some(INTEGER_SCALE); + precision = Some(INTEGER_PRECISION.into()); + scale = Some(INTEGER_SCALE.into()); } let base_name: &str = if bigquery { if base_type == BIGNUMERIC_TYPE_NAME { @@ -48,25 +50,25 @@ pub(crate) fn normalize_with_fallback( } else { base_type }; - let normalized_name: String = match (precision, scale) { + let normalized_name: String = match (&precision, &scale) { (Some(precision), Some(scale)) => format!("{base_name}({precision},{scale})"), (Some(precision), None) => format!("{base_name}({precision})"), _ => base_name.to_owned(), }; - return Some(NormalizedType { + return NormalizedType { normalized_name, family: TypeFamily::Decimal, precision, scale, length: None, - }); + }; } if FLOAT_TYPE_NAMES.contains(&base_type) { let name: &str = if bigquery { "FLOAT64" } else { base_type }; - return Some(simple(name, TypeFamily::Float)); + return simple(name, TypeFamily::Float); } if STRING_TYPE_NAMES.contains(&base_type) { - let mut length: Option = first; + let mut length: Option = first; let base_name: &str = if bigquery { "STRING" } else if snowflake && base_type == TEXT_TYPE_NAME { @@ -74,40 +76,40 @@ pub(crate) fn normalize_with_fallback( } else { base_type }; - let mut normalized_name: String = match length { + let mut normalized_name: String = match &length { Some(length) => format!("{base_name}({length})"), None => base_name.to_owned(), }; if snowflake && UNBOUNDED_TEXT_TYPE_NAMES.contains(&base_type) { - let bounded: i64 = python_or_default(length); - length = Some(bounded); + let bounded: PythonInteger = python_or_default(length); normalized_name = format!("VARCHAR({bounded})"); + length = Some(bounded); } - return Some(NormalizedType { + return NormalizedType { normalized_name, family: TypeFamily::String, precision: None, scale: None, length, - }); + }; } if BOOLEAN_TYPE_NAMES.contains(&base_type) { let name: &str = if bigquery { "BOOL" } else { base_type }; - return Some(simple(name, TypeFamily::Boolean)); + return simple(name, TypeFamily::Boolean); } if base_type.contains(TIMESTAMP_TYPE_TOKEN) { - return Some(simple( + return simple( ×tamp_normalized_name(base_type, dialect), TypeFamily::Timestamp, - )); + ); } if base_type == DATE_TYPE_NAME { - return Some(simple(base_type, TypeFamily::Date)); + return simple(base_type, TypeFamily::Date); } if base_type == DATETIME_TYPE_NAME { - return Some(simple(base_type, TypeFamily::Datetime)); + return simple(base_type, TypeFamily::Datetime); } - Some(simple(&normalized, TypeFamily::Other)) + simple(&normalized, TypeFamily::Other) } /// Python's `_timestamp_normalized_name`: Snowflake spells its timestamp aliases one way. @@ -136,17 +138,17 @@ pub(crate) fn snowflake_integer() -> NormalizedType { NormalizedType { normalized_name: format!("DECIMAL({INTEGER_PRECISION},{INTEGER_SCALE})"), family: TypeFamily::Decimal, - precision: Some(INTEGER_PRECISION), - scale: Some(INTEGER_SCALE), + precision: Some(INTEGER_PRECISION.into()), + scale: Some(INTEGER_SCALE.into()), length: None, } } /// Python's `length or DEFAULT_TEXT_LENGTH`: a missing or zero length takes the default. -pub(crate) fn python_or_default(length: Option) -> i64 { +pub(crate) fn python_or_default(length: Option) -> PythonInteger { match length { - Some(length) if length != 0 => length, - _ => DEFAULT_TEXT_LENGTH, + Some(length) if !length.is_zero() => length, + _ => DEFAULT_TEXT_LENGTH.into(), } } diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs index dc08a3b5d3..81c419168d 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs @@ -1,6 +1,5 @@ //! Python's `_normalize_with_polyglot`: the parsed type's SQL and arguments. -use polyglot_sql::ast_json::{expression_from_value, expressions_from_value}; use polyglot_sql::{ComplexityGuardOptions, DataType, Dialect, Expression, ParseOptions}; use serde_json::{Map, Value}; @@ -8,7 +7,7 @@ use crate::type_system::_helpers::fallback::{ normalize_with_fallback, python_or_default, simple, snowflake_integer, timestamp_normalized_name, }; -use crate::type_system::_helpers::python_text::split_type_and_params; +use crate::type_system::_helpers::python_text::{python_int, split_type_and_params, upper}; use crate::type_system::constants::{ BIGNUMERIC_TYPE_NAME, BOOLEAN_TYPE_NAMES, CUSTOM_NORMALIZATION_TYPE_NAMES, DATE_TYPE_NAME, DATETIME_TYPE_NAME, DECIMAL_TYPE_NAMES, FLOAT_TYPE_NAMES, FLOAT_WIRE_TYPE_NAME, @@ -16,7 +15,9 @@ use crate::type_system::constants::{ MAX_FUNCTION_CALL_DEPTH, POLYGLOT_CUSTOM_TYPE_NAME, POLYGLOT_TYPE_NAME_ALIASES, STRING_TYPE_NAMES, TIMESTAMP_TYPE_NAMES, UNBOUNDED_TEXT_TYPE_NAMES, }; -use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; +use crate::type_system::models::{ + NormalizedType, PythonInteger, TypeDialect, TypeFamily, TypeNormalizationError, +}; /// Parse one type with SQLBuild's Polyglot guard; the error text is what Python logs. pub(crate) fn parse_type(type_sql: &str, dialect: &Dialect) -> Result { @@ -33,64 +34,60 @@ pub(crate) fn parse_type(type_sql: &str, dialect: &Dialect) -> Result, -) -> Option { +) -> Result { let expression: Expression = Expression::DataType(parsed); - let Ok(generated) = polyglot.generate(&expression) else { - return None; - }; - if !generated.is_ascii() { - return None; - } - let normalized_name: String = generated.to_ascii_uppercase().replace(' ', ""); - let args: Map = expression_args(&expression)?; - let dtype_name: String = polyglot_type_name(&args)?; - let params: Vec = polyglot_type_params(&args)?; + let generated: String = polyglot + .generate(&expression) + .map_err(|error| TypeNormalizationError::Generation(error.to_string()))?; + let normalized_name: String = upper(&generated).replace(' ', ""); + let args: Map = expression_args(&expression); + let dtype_name: String = polyglot_type_name(&args); + let params: Vec = polyglot_type_params(&args); let snowflake: bool = dialect == Some(TypeDialect::Snowflake); let bigquery: bool = dialect == Some(TypeDialect::BigQuery); let dtype: &str = dtype_name.as_str(); if bigquery && INTEGER_PARSE_TYPE_NAMES.contains(&dtype) { - return Some(simple("INT64", TypeFamily::Integer)); + return Ok(simple("INT64", TypeFamily::Integer)); } if bigquery && dtype == BIGNUMERIC_TYPE_NAME { - return Some(simple("BIGNUMERIC", TypeFamily::Decimal)); + return Ok(simple("BIGNUMERIC", TypeFamily::Decimal)); } if bigquery && dtype == FLOAT_WIRE_TYPE_NAME { - return Some(simple("FLOAT64", TypeFamily::Float)); + return Ok(simple("FLOAT64", TypeFamily::Float)); } if dtype == POLYGLOT_CUSTOM_TYPE_NAME { - let raw_name: String = match args.get("name") { - Some(value) => python_str(value)?, + let raw_name: String = upper(&match args.get("name") { + Some(value) => python_str(value), None => normalized_name.clone(), - } - .to_ascii_uppercase() + }) .replace(' ', ""); if snowflake && raw_name.starts_with("NUMBER") { - return snowflake_number(&raw_name); + return Ok(snowflake_number(&raw_name)); } - return normalize_with_fallback(&raw_name, dialect); + return Ok(normalize_with_fallback(&raw_name, dialect)); } if INTEGER_TYPE_NAMES.contains(&dtype) { if snowflake { - return Some(snowflake_integer()); + return Ok(snowflake_integer()); } - return Some(simple(&normalized_name, TypeFamily::Integer)); + return Ok(simple(&normalized_name, TypeFamily::Integer)); } if DECIMAL_TYPE_NAMES.contains(&dtype) { - let mut precision: Option = params.first().copied(); - let mut scale: Option = params.get(1).copied(); + let mut precision: Option = params.first().cloned(); + let mut scale: Option = params.get(1).cloned(); let mut name: String = normalized_name; if snowflake && precision.is_none() { - precision = Some(INTEGER_PRECISION); - scale = Some(INTEGER_SCALE); + precision = Some(INTEGER_PRECISION.into()); + scale = Some(INTEGER_SCALE.into()); name = format!("DECIMAL({INTEGER_PRECISION},{INTEGER_SCALE})"); } - return Some(NormalizedType { + return Ok(NormalizedType { normalized_name: name, family: TypeFamily::Decimal, precision, @@ -99,18 +96,18 @@ pub(crate) fn normalize_parsed( }); } if FLOAT_TYPE_NAMES.contains(&dtype) { - return Some(simple(&normalized_name, TypeFamily::Float)); + return Ok(simple(&normalized_name, TypeFamily::Float)); } if STRING_TYPE_NAMES.contains(&dtype) { - let mut length: Option = params.first().copied(); + let mut length: Option = params.first().cloned(); let mut name: String = normalized_name; let base_name: &str = name.split('(').next().unwrap_or_default(); if snowflake && UNBOUNDED_TEXT_TYPE_NAMES.contains(&base_name) { - let bounded: i64 = python_or_default(length); - length = Some(bounded); + let bounded: PythonInteger = python_or_default(length); name = format!("VARCHAR({bounded})"); + length = Some(bounded); } - return Some(NormalizedType { + return Ok(NormalizedType { normalized_name: name, family: TypeFamily::String, precision: None, @@ -119,124 +116,106 @@ pub(crate) fn normalize_parsed( }); } if BOOLEAN_TYPE_NAMES.contains(&dtype) { - return Some(simple(&normalized_name, TypeFamily::Boolean)); + return Ok(simple(&normalized_name, TypeFamily::Boolean)); } if TIMESTAMP_TYPE_NAMES.contains(&dtype) { - return Some(simple( + return Ok(simple( ×tamp_normalized_name(&normalized_name, dialect), TypeFamily::Timestamp, )); } if dtype == DATE_TYPE_NAME { - return Some(simple(&normalized_name, TypeFamily::Date)); + return Ok(simple(&normalized_name, TypeFamily::Date)); } if dtype == DATETIME_TYPE_NAME { - return Some(simple(&normalized_name, TypeFamily::Datetime)); + return Ok(simple(&normalized_name, TypeFamily::Datetime)); } - Some(simple(&normalized_name, TypeFamily::Other)) + Ok(simple(&normalized_name, TypeFamily::Other)) } /// Snowflake's `NUMBER(p,s)` spelled as a custom type: Python's `DECIMAL` rewrite. -fn snowflake_number(raw_name: &str) -> Option { +fn snowflake_number(raw_name: &str) -> NormalizedType { let mut decimal_name: String = raw_name.replacen("NUMBER", "DECIMAL", 1); - let (_, params) = split_type_and_params(&decimal_name)?; - let mut precision: Option = params.first().copied(); - let mut scale: Option = params.get(1).copied(); + let (_, params) = split_type_and_params(&decimal_name); + let mut precision: Option = params.first().cloned(); + let mut scale: Option = params.get(1).cloned(); if precision.is_none() { - precision = Some(INTEGER_PRECISION); - scale = Some(INTEGER_SCALE); + precision = Some(INTEGER_PRECISION.into()); + scale = Some(INTEGER_SCALE.into()); decimal_name = format!("DECIMAL({INTEGER_PRECISION},{INTEGER_SCALE})"); } - Some(NormalizedType { + NormalizedType { normalized_name: decimal_name, family: TypeFamily::Decimal, precision, scale, length: None, - }) + } } /// The wheel's `Expression.args`: the serde payload of the expression's variant. -fn expression_args(expression: &Expression) -> Option> { - let Ok(value) = serde_json::to_value(expression) else { - return None; - }; - match value { - Value::Object(map) => match map.into_iter().next() { - Some((_, Value::Object(payload))) => Some(payload), - _ => Some(Map::new()), +fn expression_args(expression: &Expression) -> Map { + match serde_json::to_value(expression) { + Ok(Value::Object(map)) => match map.into_iter().next() { + Some((_, Value::Object(payload))) => payload, + _ => Map::new(), }, - _ => Some(Map::new()), + _ => Map::new(), } } /// Python's `_polyglot_type_name`. -fn polyglot_type_name(args: &Map) -> Option { - let data_type: String = match args.get("data_type") { - Some(value) => python_str(value)?, +fn polyglot_type_name(args: &Map) -> String { + let data_type: String = upper(&match args.get("data_type") { + Some(value) => python_str(value), None => String::new(), - } - .to_ascii_uppercase(); + }); if let Some((_, alias)) = POLYGLOT_TYPE_NAME_ALIASES .iter() .find(|(name, _)| *name == data_type) { - return Some((*alias).to_owned()); + return (*alias).to_owned(); } if data_type == POLYGLOT_CUSTOM_TYPE_NAME { - let name: String = match args.get("name") { - Some(value) => python_str(value)?, + let name: String = upper(&match args.get("name") { + Some(value) => python_str(value), None => String::new(), - } - .to_ascii_uppercase() + }) .replace(' ', ""); if CUSTOM_NORMALIZATION_TYPE_NAMES.contains(&name.as_str()) { - return Some(name); + return name; } } - Some(data_type) + data_type } -/// Python's `_polyglot_type_params`; text and integers beyond `i64` defer. -fn polyglot_type_params(args: &Map) -> Option> { - let mut params: Vec = Vec::new(); +/// Python's `_polyglot_type_params`: `int(value)` of each present parameter, skipping what +/// `int` rejects. +fn polyglot_type_params(args: &Map) -> Vec { + let mut params: Vec = Vec::new(); for key in ["precision", "scale", "length"] { let Some(value) = args.get(key) else { continue; }; - match value { - Value::Null | Value::Array(_) | Value::Object(_) => {} - Value::Bool(flag) => params.push(i64::from(*flag)), - Value::Number(number) => params.push(number.as_i64()?), - Value::String(_) if is_python_expression(value) => {} - Value::String(_) => return None, - } + let param: Option = match value { + Value::Null | Value::Array(_) | Value::Object(_) => None, + Value::Bool(flag) => Some(i64::from(*flag).into()), + Value::Number(number) => python_int(&number.to_string()), + Value::String(text) => python_int(text), + }; + params.extend(param); } - Some(params) + params } -/// Python's `str(value)` for the text values Polyglot returns; None for anything else. -fn python_str(value: &Value) -> Option { - if is_python_expression(value) { - return None; - } +/// Python's `str(value)` for the scalar values Polyglot gives a parsed type's tag and name. +fn python_str(value: &Value) -> String { match value { - Value::String(text) if text.is_ascii() => Some(text.clone()), - Value::Null => Some("None".to_owned()), - _ => None, + Value::String(text) => text.clone(), + Value::Null => "None".to_owned(), + Value::Bool(true) => "True".to_owned(), + Value::Bool(false) => "False".to_owned(), + Value::Number(number) => number.to_string(), + Value::Array(_) | Value::Object(_) => value.to_string(), } } - -/// Whether the wheel hands Python an expression object; every variant holds data, so not a scalar. -fn is_python_expression(value: &Value) -> bool { - if !matches!(value, Value::Object(_) | Value::Array(_)) { - return false; - } - if let Ok(_expression) = expression_from_value(value.clone()) { - return true; - } - let Ok(_expressions) = expressions_from_value(value.clone()) else { - return false; - }; - true -} diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs index a1dda52dbf..7df7abd930 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs @@ -1,100 +1,81 @@ -//! Python string and integer semantics the type normalization relies on, for ASCII text. +//! Python string and integer semantics the type normalization relies on. -use crate::type_system::constants::PYTHON_ASCII_WHITESPACE; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_space::is_python_space; +use sqlbuild_core::text::main::python_decimal_value::python_decimal_value; +use sqlbuild_core::text::main::python_strip::python_strip; +use sqlbuild_core::text::main::python_upper::python_upper; +use sqlbuild_core::text::models::PythonText; -/// The result of Python's `int(text)` on one parameter. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum PythonInt { - Value(i64), - Invalid, - /// A valid integer outside `i64`; Python keeps it, so the caller defers. - TooLarge, -} - -pub(crate) fn is_python_space(byte: u8) -> bool { - PYTHON_ASCII_WHITESPACE.contains(&byte) -} +use crate::type_system::models::PythonInteger; -/// Python's `str.strip()`. -pub(crate) fn python_strip(text: &str) -> &str { - text.trim_matches(|character: char| character.is_ascii() && is_python_space(character as u8)) +/// Python's `text.upper()` under the running Python's Unicode version. +pub(crate) fn upper(text: &str) -> String { + python_upper(active_python_text(), text) } /// Python's `re.sub(r"\s+", "", text)`. pub(crate) fn remove_python_space(text: &str) -> String { text.chars() - .filter(|character| !(character.is_ascii() && is_python_space(*character as u8))) + .filter(|character| !is_python_space(*character)) .collect() } -/// Python's base-10 `int(text)`: whitespace, a sign, and single underscores between digits. -pub(crate) fn python_int(text: &str) -> PythonInt { +/// Python's base-10 `int(text)`: whitespace, a sign, Unicode decimal digits and single +/// underscores between digits; None where Python raises `ValueError`. +pub(crate) fn python_int(text: &str) -> Option { + let python: PythonText = active_python_text(); let stripped: &str = python_strip(text); - let (negative, digits) = match stripped.as_bytes().first() { - Some(b'-') => (true, &stripped[1..]), - Some(b'+') => (false, &stripped[1..]), + let (negative, digits) = match stripped.chars().next() { + Some('-') => (true, &stripped[1..]), + Some('+') => (false, &stripped[1..]), _ => (false, stripped), }; - let bytes: &[u8] = digits.as_bytes(); - let valid: bool = !bytes.is_empty() - && bytes.first().is_some_and(u8::is_ascii_digit) - && bytes.last().is_some_and(u8::is_ascii_digit) - && bytes - .iter() - .all(|byte| byte.is_ascii_digit() || *byte == b'_') - && !digits.contains("__"); - if !valid { - return PythonInt::Invalid; - } - let mut value: i64 = 0; - for byte in bytes.iter().filter(|byte| byte.is_ascii_digit()) { - let digit: i64 = i64::from(byte - b'0'); - let next: Option = value.checked_mul(10).and_then(|scaled| { - if negative { - scaled.checked_sub(digit) - } else { - scaled.checked_add(digit) + let mut value: String = String::with_capacity(digits.len() + 1); + let mut previous_underscore: bool = true; + for character in digits.chars() { + if character == '_' { + if previous_underscore { + return None; } - }); - let Some(next) = next else { - return PythonInt::TooLarge; - }; - value = next; + previous_underscore = true; + continue; + } + let digit: u32 = python_decimal_value(python, character)?; + value.push(char::from_digit(digit, 10)?); + previous_underscore = false; + } + if value.is_empty() || previous_underscore { + return None; } - PythonInt::Value(value) + Some(PythonInteger::from_digits(negative, &value)) } -/// Python's `_split_type_and_params`; None for a parameter beyond `i64`. -pub(crate) fn split_type_and_params(type_sql: &str) -> Option<(&str, Vec)> { +/// Python's `_split_type_and_params`: the regex `^([A-Z0-9_]+)(?:\(([^)]*)\))?$`, then +/// `int(part.strip())` for each comma-separated parameter, skipping what `int` rejects. +pub(crate) fn split_type_and_params(type_sql: &str) -> (&str, Vec) { let candidate: &str = type_sql.strip_suffix('\n').unwrap_or(type_sql); let name_end: usize = candidate .bytes() .position(|byte| !(byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_')) .unwrap_or(candidate.len()); if name_end == 0 { - return Some((type_sql, Vec::new())); + return (type_sql, Vec::new()); } let (name, rest) = candidate.split_at(name_end); if rest.is_empty() { - return Some((name, Vec::new())); + return (name, Vec::new()); } let Some(raw_params) = rest .strip_prefix('(') .and_then(|inner| inner.strip_suffix(')')) .filter(|inner| !inner.contains(')')) else { - return Some((type_sql, Vec::new())); + return (type_sql, Vec::new()); }; - let mut params: Vec = Vec::new(); if raw_params.is_empty() { - return Some((name, params)); - } - for raw_part in raw_params.split(',') { - match python_int(raw_part) { - PythonInt::Value(value) => params.push(value), - PythonInt::Invalid => {} - PythonInt::TooLarge => return None, - } + return (name, Vec::new()); } - Some((name, params)) + let params: Vec = raw_params.split(',').filter_map(python_int).collect(); + (name, params) } diff --git a/crates/sqlbuild-analysis/src/type_system/constants.rs b/crates/sqlbuild-analysis/src/type_system/constants.rs index 9e05c81f24..db367e2254 100644 --- a/crates/sqlbuild-analysis/src/type_system/constants.rs +++ b/crates/sqlbuild-analysis/src/type_system/constants.rs @@ -33,9 +33,9 @@ pub(crate) const POLYGLOT_TYPE_NAME_ALIASES: &[(&str, &str)] = &[ ("TINY_INT", "TINYINT"), ("VAR_CHAR", "VARCHAR"), ]; -/// The most `[` a type may hold before Python decides it; Polyglot's guards do not bound `[]`. -pub(crate) const MAX_TYPE_BRACKETS: usize = 32; +/// Brackets a type may nest before normalization moves to a stack sized for its depth. +pub(crate) const SHALLOW_TYPE_NESTING: usize = 256; +/// Stack reserved per nesting level of a deeply nested type: parse, write back, read and drop. +pub(crate) const NESTED_TYPE_STACK_BYTES: usize = 64 * 1024; /// The function call depth limit SQLBuild passes to every Polyglot parse. pub(crate) const MAX_FUNCTION_CALL_DEPTH: usize = 512; -/// Characters Python's `str.strip()` and the `\s` regex class treat as ASCII whitespace. -pub(crate) const PYTHON_ASCII_WHITESPACE: &[u8] = b" \t\n\r\x0b\x0c\x1c\x1d\x1e\x1f"; diff --git a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs index 466d0d2186..1f52a6b068 100644 --- a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs @@ -1,29 +1,52 @@ //! Python's `normalize_type`: Polyglot's parsed type first, then the text fallback. -use crate::type_system::_helpers::dialects::{coerce_type_dialect, polyglot_dialect}; +use polyglot_sql::{Dialect, DialectType}; + +use crate::type_system::_helpers::dialects::coerce_type_dialect; use crate::type_system::_helpers::fallback::normalize_with_fallback; use crate::type_system::_helpers::parsed::{normalize_parsed, parse_type}; -use crate::type_system::constants::MAX_TYPE_BRACKETS; -use crate::type_system::models::{TypeDialect, TypeNormalization}; +use crate::type_system::constants::{NESTED_TYPE_STACK_BYTES, SHALLOW_TYPE_NESTING}; +use crate::type_system::models::{TypeDialect, TypeNormalization, TypeNormalizationError}; -/// Normalize one type as Python does for the dialect name it hands Polyglot, or None to defer. -#[must_use] -pub fn normalize_type(type_sql: &str, dialect: &str) -> Option { - if !type_sql.is_ascii() - || !dialect.is_ascii() - || type_sql.bytes().filter(|byte| *byte == b'[').count() > MAX_TYPE_BRACKETS - { - return None; +/// Normalize one type as Python does for the dialect name it hands Polyglot. +/// +/// # Errors +/// +/// Python's exception where it raises: an unknown dialect or an unwritable parsed type. +pub fn normalize_type( + type_sql: &str, + dialect: &str, +) -> Result { + let nesting: usize = type_sql + .bytes() + .filter(|byte| matches!(byte, b'[' | b'<' | b'(')) + .count(); + if nesting <= SHALLOW_TYPE_NESTING { + return normalized(type_sql, dialect); } - let polyglot = polyglot_dialect(dialect)?; + stacker::grow(nesting.saturating_mul(NESTED_TYPE_STACK_BYTES), || { + normalized(type_sql, dialect) + }) +} + +fn normalized(type_sql: &str, dialect: &str) -> Result { + let name: &str = if dialect.is_empty() { + "generic" + } else { + dialect + }; + let polyglot: Dialect = name + .parse::() + .map(Dialect::get) + .map_err(|_| TypeNormalizationError::UnknownDialect(name.to_owned()))?; let type_dialect: Option = coerce_type_dialect(dialect); match parse_type(type_sql, &polyglot) { - Ok(parsed) => Some(TypeNormalization { + Ok(parsed) => Ok(TypeNormalization { normalized: normalize_parsed(parsed, &polyglot, type_dialect)?, parse_error: None, }), - Err(error) => Some(TypeNormalization { - normalized: normalize_with_fallback(type_sql, type_dialect)?, + Err(error) => Ok(TypeNormalization { + normalized: normalize_with_fallback(type_sql, type_dialect), parse_error: Some(error), }), } diff --git a/crates/sqlbuild-analysis/src/type_system/models.rs b/crates/sqlbuild-analysis/src/type_system/models.rs index 0250342b03..d420cce531 100644 --- a/crates/sqlbuild-analysis/src/type_system/models.rs +++ b/crates/sqlbuild-analysis/src/type_system/models.rs @@ -45,14 +45,78 @@ pub enum TypeDialect { Tsql, } +/// A Python `int` of any size, held as its canonical base-10 text. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PythonInteger(String); + +impl PythonInteger { + /// The integer whose sign and base-10 digits are given. + #[must_use] + pub fn from_digits(negative: bool, digits: &str) -> Self { + let significant: &str = digits.trim_start_matches('0'); + if significant.is_empty() { + return Self("0".to_owned()); + } + Self(if negative { + format!("-{significant}") + } else { + significant.to_owned() + }) + } + + /// Whether the integer is zero, so Python treats it as false. + #[must_use] + pub fn is_zero(&self) -> bool { + self.0 == "0" + } + + /// The integer's canonical base-10 text, as Python's `str(value)` spells it. + #[must_use] + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl From for PythonInteger { + fn from(value: i64) -> Self { + Self(value.to_string()) + } +} + +impl std::fmt::Display for PythonInteger { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str(&self.0) + } +} + /// One type string's comparison shape. #[derive(Debug, Clone, PartialEq, Eq)] pub struct NormalizedType { pub normalized_name: String, pub family: TypeFamily, - pub precision: Option, - pub scale: Option, - pub length: Option, + pub precision: Option, + pub scale: Option, + pub length: Option, +} + +/// Why Python's `normalize_type` raises instead of returning a type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TypeNormalizationError { + /// Polyglot does not know the dialect name: the wheel's `ValueError`. + UnknownDialect(String), + /// Polyglot cannot write the parsed type back as SQL: the wheel's `PolyglotError`. + Generation(String), +} + +impl TypeNormalizationError { + /// The message Python's exception carries. + #[must_use] + pub fn message(&self) -> String { + match self { + Self::UnknownDialect(name) => format!("Unknown dialect: {name}"), + Self::Generation(message) => message.clone(), + } + } } /// A normalized type and the Polyglot parse error Python logs before its text fallback. diff --git a/crates/sqlbuild-analysis/src/type_system/tests/helpers.rs b/crates/sqlbuild-analysis/src/type_system/tests/helpers.rs index 300ca546a8..cc51b6f245 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/helpers.rs @@ -1,15 +1,16 @@ -use crate::type_system::models::TypeNormalization; +use crate::type_system::models::{PythonInteger, TypeNormalization}; pub(super) fn normalization_text(normalization: &TypeNormalization) -> String { - let optional = |value: Option| value.map_or("-".to_owned(), |value| value.to_string()); + let optional = + |value: &Option| value.as_ref().map_or("-".to_owned(), ToString::to_string); let normalized = &normalization.normalized; format!( "{} | {} | {} | {} | {} | {}", normalized.normalized_name, normalized.family.as_str(), - optional(normalized.precision), - optional(normalized.scale), - optional(normalized.length), + optional(&normalized.precision), + optional(&normalized.scale), + optional(&normalized.length), normalization.parse_error.as_deref().unwrap_or("-"), ) } diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs index 63ea20f15e..1c415b32d1 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs @@ -1,4 +1,5 @@ use crate::type_system::main::normalize_type::normalize_type; +use crate::type_system::models::TypeNormalizationError; use crate::type_system::tests::helpers::normalization_text; use crate::type_system::tests::test_types::{BracketDepthTestCase, NormalizeTypeTestCase}; @@ -9,87 +10,95 @@ fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { description: "integer aliases keep their spelling outside Snowflake and BigQuery", type_sql: "integer", dialect: "duckdb", - expected_normalization: Some("INT | integer | - | - | - | -"), + expected_normalization: Ok("INT | integer | - | - | - | -"), }, NormalizeTypeTestCase { description: "BigQuery integers are INT64", type_sql: "INT", dialect: "bigquery", - expected_normalization: Some("INT64 | integer | - | - | - | -"), + expected_normalization: Ok("INT64 | integer | - | - | - | -"), }, NormalizeTypeTestCase { description: "Snowflake integers are DECIMAL(38,0)", type_sql: "BIGINT", dialect: "snowflake", - expected_normalization: Some("DECIMAL(38,0) | decimal | 38 | 0 | - | -"), + expected_normalization: Ok("DECIMAL(38,0) | decimal | 38 | 0 | - | -"), }, NormalizeTypeTestCase { description: "Snowflake NUMBER is a custom type rewritten to DECIMAL", type_sql: "NUMBER(12, 2)", dialect: "snowflake", - expected_normalization: Some("DECIMAL(12,2) | decimal | 12 | 2 | - | -"), + expected_normalization: Ok("DECIMAL(12,2) | decimal | 12 | 2 | - | -"), }, NormalizeTypeTestCase { description: "Snowflake NUMBER without precision takes the integer default", type_sql: "NUMBER", dialect: "snowflake", - expected_normalization: Some("DECIMAL(38,0) | decimal | 38 | 0 | - | -"), + expected_normalization: Ok("DECIMAL(38,0) | decimal | 38 | 0 | - | -"), }, NormalizeTypeTestCase { description: "Snowflake unbounded text takes the default length", type_sql: "TEXT", dialect: "snowflake", - expected_normalization: Some("VARCHAR(16777216) | string | - | - | 16777216 | -"), + expected_normalization: Ok("VARCHAR(16777216) | string | - | - | 16777216 | -"), }, NormalizeTypeTestCase { description: "Snowflake timestamp aliases", type_sql: "TIMESTAMP_NTZ", dialect: "snowflake", - expected_normalization: Some("TIMESTAMP_NTZ | timestamp | - | - | - | -"), + expected_normalization: Ok("TIMESTAMP_NTZ | timestamp | - | - | - | -"), }, NormalizeTypeTestCase { description: "decimal precision and scale", type_sql: "decimal(10, 2)", dialect: "duckdb", - expected_normalization: Some("DECIMAL(10,2) | decimal | 10 | 2 | - | -"), + expected_normalization: Ok("DECIMAL(10,2) | decimal | 10 | 2 | - | -"), }, NormalizeTypeTestCase { description: "nested types are other", type_sql: "ARRAY>", dialect: "bigquery", - expected_normalization: Some("ARRAY> | other | - | - | - | -"), + expected_normalization: Ok("ARRAY> | other | - | - | - | -"), }, NormalizeTypeTestCase { description: "a parse failure falls back to the text and reports the error", type_sql: "order status", dialect: "generic", - expected_normalization: Some( + expected_normalization: Ok( "ORDERSTATUS | other | - | - | - | Parse error at line 1, column 13: Unexpected token after data type: status", ), }, NormalizeTypeTestCase { - description: "a dialect this build does not include defers", + description: "every Polyglot dialect is normalized natively", type_sql: "INT", dialect: "mysql", - expected_normalization: None, + expected_normalization: Ok("INT | integer | - | - | - | -"), }, NormalizeTypeTestCase { - description: "a dialect Polyglot does not know defers so Python raises", + description: "a dialect Polyglot does not know raises Python's error", type_sql: "INT", dialect: "motherduck", - expected_normalization: None, + expected_normalization: Err("Unknown dialect: motherduck"), }, NormalizeTypeTestCase { - description: "non-ASCII text defers", - type_sql: "caf\u{e9}", + description: "non-ASCII text upper-cases with Python's Unicode mapping", + type_sql: "stra\u{df}e", dialect: "generic", - expected_normalization: None, + expected_normalization: Ok("STRASSE | other | - | - | - | -"), }, NormalizeTypeTestCase { - description: "a parameter beyond i64 defers", - type_sql: "order status(99999999999999999999)", - dialect: "generic", - expected_normalization: None, + description: "a parameter beyond i64 keeps Python's integer", + type_sql: "DECIMAL(99999999999999999999, 2)", + dialect: "duckdb", + expected_normalization: Ok( + "DECIMAL(99999999999999999999,2) | decimal | 99999999999999999999 | 2 | - | -", + ), + }, + NormalizeTypeTestCase { + description: "Unicode decimal digits are Python integers", + type_sql: "VARCHAR(\u{663})", + dialect: "snowflake", + expected_normalization: Ok("VARCHAR(3) | string | - | - | 3 | -"), }, ]; @@ -98,7 +107,10 @@ fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { normalize_type(test_case.type_sql, test_case.dialect) .as_ref() .map(normalization_text) - .as_deref(), + .map_err(TypeNormalizationError::message) + .as_ref() + .map(String::as_str) + .map_err(String::as_str), test_case.expected_normalization, "{}", test_case.description @@ -107,22 +119,17 @@ fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { } #[test] -fn given_array_suffixes_when_normalizing_on_a_small_stack_then_deep_types_defer() { +fn given_array_suffixes_when_normalizing_on_a_small_stack_then_deep_types_are_normalized() { let test_cases = [ BracketDepthTestCase { - description: "at the bracket cap, parsed on a 1 MiB debug-build stack", + description: "shallow types stay on the caller's stack", depth: 32, expected_native: true, }, BracketDepthTestCase { - description: "one past the bracket cap", - depth: 33, - expected_native: false, - }, - BracketDepthTestCase { - description: "far past the bracket cap", + description: "deep types move to a stack sized for their depth", depth: 100_000, - expected_native: false, + expected_native: true, }, ]; @@ -130,7 +137,7 @@ fn given_array_suffixes_when_normalizing_on_a_small_stack_then_deep_types_defer( let type_sql: String = format!("INT{}", "[]".repeat(test_case.depth)); let answered: bool = std::thread::Builder::new() .stack_size(1024 * 1024) - .spawn(move || normalize_type(&type_sql, "generic").is_some()) + .spawn(move || normalize_type(&type_sql, "generic").is_ok()) .expect("the test thread starts") .join() .expect("normalization does not overflow the stack"); diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_python_text.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_python_text.rs index aec6520811..d695a9b51b 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_python_text.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_python_text.rs @@ -1,4 +1,5 @@ -use crate::type_system::_helpers::python_text::{PythonInt, python_int, split_type_and_params}; +use crate::type_system::_helpers::python_text::{python_int, split_type_and_params}; +use crate::type_system::models::PythonInteger; use crate::type_system::tests::test_types::{PythonIntTestCase, SplitTypeTestCase}; #[test] @@ -7,49 +8,49 @@ fn given_parameter_text_when_parsing_as_python_int_then_python_result_is_returne PythonIntTestCase { description: "padded digits", text: " \t42\x1c", - expected_value: PythonInt::Value(42), + expected_value: Some(42), }, PythonIntTestCase { description: "signs", text: "-7", - expected_value: PythonInt::Value(-7), + expected_value: Some(-7), }, PythonIntTestCase { description: "single underscores between digits", text: "+1_000", - expected_value: PythonInt::Value(1000), + expected_value: Some(1000), }, PythonIntTestCase { description: "double underscores", text: "1__0", - expected_value: PythonInt::Invalid, + expected_value: None, }, PythonIntTestCase { description: "leading underscore", text: "_1", - expected_value: PythonInt::Invalid, + expected_value: None, }, PythonIntTestCase { description: "words", text: "MAX", - expected_value: PythonInt::Invalid, + expected_value: None, }, PythonIntTestCase { description: "the smallest i64", text: "-9223372036854775808", - expected_value: PythonInt::Value(i64::MIN), + expected_value: Some(i64::MIN), }, PythonIntTestCase { - description: "beyond i64", - text: "9223372036854775808", - expected_value: PythonInt::TooLarge, + description: "Unicode decimal digits", + text: "\u{663}\u{661}", + expected_value: Some(31), }, ]; for test_case in test_cases { assert_eq!( python_int(test_case.text), - test_case.expected_value, + test_case.expected_value.map(PythonInteger::from), "{}", test_case.description ); @@ -62,39 +63,56 @@ fn given_type_text_when_splitting_then_python_name_and_parameters_are_returned() SplitTypeTestCase { description: "name only", type_sql: "VARCHAR", - expected_split: Some(("VARCHAR", vec![])), + expected_split: ("VARCHAR", vec![]), }, SplitTypeTestCase { description: "parameters skip what int() rejects", type_sql: "DECIMAL(10,X,2)", - expected_split: Some(("DECIMAL", vec![10, 2])), + expected_split: ("DECIMAL", vec!["10", "2"]), }, SplitTypeTestCase { description: "a final newline is allowed by the regex end anchor", type_sql: "NUMBER(5)\n", - expected_split: Some(("NUMBER", vec![5])), + expected_split: ("NUMBER", vec!["5"]), }, SplitTypeTestCase { description: "anything else keeps the whole text", type_sql: "ARRAY", - expected_split: Some(("ARRAY", vec![])), + expected_split: ("ARRAY", vec![]), }, SplitTypeTestCase { description: "nested parentheses keep the whole text", type_sql: "A(1)(2)", - expected_split: Some(("A(1)(2)", vec![])), + expected_split: ("A(1)(2)", vec![]), }, SplitTypeTestCase { - description: "a parameter beyond i64 defers", + description: "a parameter beyond i64 keeps Python's integer", type_sql: "A(99999999999999999999)", - expected_split: None, + expected_split: ("A", vec!["99999999999999999999"]), }, ]; for test_case in test_cases { assert_eq!( - split_type_and_params(test_case.type_sql), - test_case.expected_split, + { + let (name, params) = split_type_and_params(test_case.type_sql); + ( + name, + params + .iter() + .map(|param| param.as_str().to_owned()) + .collect::>(), + ) + }, + ( + test_case.expected_split.0, + test_case + .expected_split + .1 + .into_iter() + .map(str::to_owned) + .collect::>() + ), "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs index 6044e68168..f731088453 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs @@ -1,23 +1,21 @@ -use crate::type_system::_helpers::python_text::PythonInt; - pub(super) struct NormalizeTypeTestCase { pub(super) description: &'static str, pub(super) type_sql: &'static str, pub(super) dialect: &'static str, - /// `name | family | precision | scale | length | parse error`, or None to defer. - pub(super) expected_normalization: Option<&'static str>, + /// `name | family | precision | scale | length | parse error`, or Python's error. + pub(super) expected_normalization: Result<&'static str, &'static str>, } pub(super) struct PythonIntTestCase { pub(super) description: &'static str, pub(super) text: &'static str, - pub(super) expected_value: PythonInt, + pub(super) expected_value: Option, } pub(super) struct SplitTypeTestCase { pub(super) description: &'static str, pub(super) type_sql: &'static str, - pub(super) expected_split: Option<(&'static str, Vec)>, + pub(super) expected_split: (&'static str, Vec<&'static str>), } pub(super) struct BracketDepthTestCase { diff --git a/crates/sqlbuild-core/src/text/_helpers/case_tables.rs b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs new file mode 100644 index 0000000000..07b7f05015 --- /dev/null +++ b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs @@ -0,0 +1,28 @@ +//! The generated `str.casefold()` and `str.upper()` mappings, per Unicode version. + +/// `(unicodedata.unidata_version, mappings)` of every character `str.casefold()` changes. +pub(crate) const CASEFOLD_TABLES: [(&str, &[(u32, &str)]); 3] = [ + ("15.0.0", include!("../casefold_mappings/unicode_15_0_0.in")), + ("15.1.0", include!("../casefold_mappings/unicode_15_1_0.in")), + ("16.0.0", include!("../casefold_mappings/unicode_16_0_0.in")), +]; + +/// `(unicodedata.unidata_version, mappings)` of every character `str.upper()` changes. +pub(crate) const UPPER_TABLES: [(&str, &[(u32, &str)]); 3] = [ + ("15.0.0", include!("../upper_mappings/unicode_15_0_0.in")), + ("15.1.0", include!("../upper_mappings/unicode_15_1_0.in")), + ("16.0.0", include!("../upper_mappings/unicode_16_0_0.in")), +]; + +/// One string with each character replaced by its `mappings` entry, if it has one. +pub(crate) fn mapped(mappings: &[(u32, &str)], text: &str) -> String { + let mut result: String = String::with_capacity(text.len()); + for character in text.chars() { + let code_point: u32 = u32::from(character); + match mappings.binary_search_by_key(&code_point, |(key, _)| *key) { + Ok(index) => result.push_str(mappings[index].1), + Err(_) => result.push(character), + } + } + result +} diff --git a/crates/sqlbuild-core/src/text/_helpers/mod.rs b/crates/sqlbuild-core/src/text/_helpers/mod.rs index ede18696f6..017b3bff3e 100644 --- a/crates/sqlbuild-core/src/text/_helpers/mod.rs +++ b/crates/sqlbuild-core/src/text/_helpers/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod alnum_tables; +pub(crate) mod case_tables; pub(crate) mod cleandoc; pub(crate) mod code_point_ranges; pub(crate) mod sequence_matcher; diff --git a/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_0_0.in b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_0_0.in new file mode 100644 index 0000000000..c1553126bb --- /dev/null +++ b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_0_0.in @@ -0,0 +1,1534 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.casefold()` changes in Unicode 15.0.0, in code point order. +&[ + (0x0041, "\u{61}"), + (0x0042, "\u{62}"), + (0x0043, "\u{63}"), + (0x0044, "\u{64}"), + (0x0045, "\u{65}"), + (0x0046, "\u{66}"), + (0x0047, "\u{67}"), + (0x0048, "\u{68}"), + (0x0049, "\u{69}"), + (0x004A, "\u{6a}"), + (0x004B, "\u{6b}"), + (0x004C, "\u{6c}"), + (0x004D, "\u{6d}"), + (0x004E, "\u{6e}"), + (0x004F, "\u{6f}"), + (0x0050, "\u{70}"), + (0x0051, "\u{71}"), + (0x0052, "\u{72}"), + (0x0053, "\u{73}"), + (0x0054, "\u{74}"), + (0x0055, "\u{75}"), + (0x0056, "\u{76}"), + (0x0057, "\u{77}"), + (0x0058, "\u{78}"), + (0x0059, "\u{79}"), + (0x005A, "\u{7a}"), + (0x00B5, "\u{3bc}"), + (0x00C0, "\u{e0}"), + (0x00C1, "\u{e1}"), + (0x00C2, "\u{e2}"), + (0x00C3, "\u{e3}"), + (0x00C4, "\u{e4}"), + (0x00C5, "\u{e5}"), + (0x00C6, "\u{e6}"), + (0x00C7, "\u{e7}"), + (0x00C8, "\u{e8}"), + (0x00C9, "\u{e9}"), + (0x00CA, "\u{ea}"), + (0x00CB, "\u{eb}"), + (0x00CC, "\u{ec}"), + (0x00CD, "\u{ed}"), + (0x00CE, "\u{ee}"), + (0x00CF, "\u{ef}"), + (0x00D0, "\u{f0}"), + (0x00D1, "\u{f1}"), + (0x00D2, "\u{f2}"), + (0x00D3, "\u{f3}"), + (0x00D4, "\u{f4}"), + (0x00D5, "\u{f5}"), + (0x00D6, "\u{f6}"), + (0x00D8, "\u{f8}"), + (0x00D9, "\u{f9}"), + (0x00DA, "\u{fa}"), + (0x00DB, "\u{fb}"), + (0x00DC, "\u{fc}"), + (0x00DD, "\u{fd}"), + (0x00DE, "\u{fe}"), + (0x00DF, "\u{73}\u{73}"), + (0x0100, "\u{101}"), + (0x0102, "\u{103}"), + (0x0104, "\u{105}"), + (0x0106, "\u{107}"), + (0x0108, "\u{109}"), + (0x010A, "\u{10b}"), + (0x010C, "\u{10d}"), + (0x010E, "\u{10f}"), + (0x0110, "\u{111}"), + (0x0112, "\u{113}"), + (0x0114, "\u{115}"), + (0x0116, "\u{117}"), + (0x0118, "\u{119}"), + (0x011A, "\u{11b}"), + (0x011C, "\u{11d}"), + (0x011E, "\u{11f}"), + (0x0120, "\u{121}"), + (0x0122, "\u{123}"), + (0x0124, "\u{125}"), + (0x0126, "\u{127}"), + (0x0128, "\u{129}"), + (0x012A, "\u{12b}"), + (0x012C, "\u{12d}"), + (0x012E, "\u{12f}"), + (0x0130, "\u{69}\u{307}"), + (0x0132, "\u{133}"), + (0x0134, "\u{135}"), + (0x0136, "\u{137}"), + (0x0139, "\u{13a}"), + (0x013B, "\u{13c}"), + (0x013D, "\u{13e}"), + (0x013F, "\u{140}"), + (0x0141, "\u{142}"), + (0x0143, "\u{144}"), + (0x0145, "\u{146}"), + (0x0147, "\u{148}"), + (0x0149, "\u{2bc}\u{6e}"), + (0x014A, "\u{14b}"), + (0x014C, "\u{14d}"), + (0x014E, "\u{14f}"), + (0x0150, "\u{151}"), + (0x0152, "\u{153}"), + (0x0154, "\u{155}"), + (0x0156, "\u{157}"), + (0x0158, "\u{159}"), + (0x015A, "\u{15b}"), + (0x015C, "\u{15d}"), + (0x015E, "\u{15f}"), + (0x0160, "\u{161}"), + (0x0162, "\u{163}"), + (0x0164, "\u{165}"), + (0x0166, "\u{167}"), + (0x0168, "\u{169}"), + (0x016A, "\u{16b}"), + (0x016C, "\u{16d}"), + (0x016E, "\u{16f}"), + (0x0170, "\u{171}"), + (0x0172, "\u{173}"), + (0x0174, "\u{175}"), + (0x0176, "\u{177}"), + (0x0178, "\u{ff}"), + (0x0179, "\u{17a}"), + (0x017B, "\u{17c}"), + (0x017D, "\u{17e}"), + (0x017F, "\u{73}"), + (0x0181, "\u{253}"), + (0x0182, "\u{183}"), + (0x0184, "\u{185}"), + (0x0186, "\u{254}"), + (0x0187, "\u{188}"), + (0x0189, "\u{256}"), + (0x018A, "\u{257}"), + (0x018B, "\u{18c}"), + (0x018E, "\u{1dd}"), + (0x018F, "\u{259}"), + (0x0190, "\u{25b}"), + (0x0191, "\u{192}"), + (0x0193, "\u{260}"), + (0x0194, "\u{263}"), + (0x0196, "\u{269}"), + (0x0197, "\u{268}"), + (0x0198, "\u{199}"), + (0x019C, "\u{26f}"), + (0x019D, "\u{272}"), + (0x019F, "\u{275}"), + (0x01A0, "\u{1a1}"), + (0x01A2, "\u{1a3}"), + (0x01A4, "\u{1a5}"), + (0x01A6, "\u{280}"), + (0x01A7, "\u{1a8}"), + (0x01A9, "\u{283}"), + (0x01AC, "\u{1ad}"), + (0x01AE, "\u{288}"), + (0x01AF, "\u{1b0}"), + (0x01B1, "\u{28a}"), + (0x01B2, "\u{28b}"), + (0x01B3, "\u{1b4}"), + (0x01B5, "\u{1b6}"), + (0x01B7, "\u{292}"), + (0x01B8, "\u{1b9}"), + (0x01BC, "\u{1bd}"), + (0x01C4, "\u{1c6}"), + (0x01C5, "\u{1c6}"), + (0x01C7, "\u{1c9}"), + (0x01C8, "\u{1c9}"), + (0x01CA, "\u{1cc}"), + (0x01CB, "\u{1cc}"), + (0x01CD, "\u{1ce}"), + (0x01CF, "\u{1d0}"), + (0x01D1, "\u{1d2}"), + (0x01D3, "\u{1d4}"), + (0x01D5, "\u{1d6}"), + (0x01D7, "\u{1d8}"), + (0x01D9, "\u{1da}"), + (0x01DB, "\u{1dc}"), + (0x01DE, "\u{1df}"), + (0x01E0, "\u{1e1}"), + (0x01E2, "\u{1e3}"), + (0x01E4, "\u{1e5}"), + (0x01E6, "\u{1e7}"), + (0x01E8, "\u{1e9}"), + (0x01EA, "\u{1eb}"), + (0x01EC, "\u{1ed}"), + (0x01EE, "\u{1ef}"), + (0x01F0, "\u{6a}\u{30c}"), + (0x01F1, "\u{1f3}"), + (0x01F2, "\u{1f3}"), + (0x01F4, "\u{1f5}"), + (0x01F6, "\u{195}"), + (0x01F7, "\u{1bf}"), + (0x01F8, "\u{1f9}"), + (0x01FA, "\u{1fb}"), + (0x01FC, "\u{1fd}"), + (0x01FE, "\u{1ff}"), + (0x0200, "\u{201}"), + (0x0202, "\u{203}"), + (0x0204, "\u{205}"), + (0x0206, "\u{207}"), + (0x0208, "\u{209}"), + (0x020A, "\u{20b}"), + (0x020C, "\u{20d}"), + (0x020E, "\u{20f}"), + (0x0210, "\u{211}"), + (0x0212, "\u{213}"), + (0x0214, "\u{215}"), + (0x0216, "\u{217}"), + (0x0218, "\u{219}"), + (0x021A, "\u{21b}"), + (0x021C, "\u{21d}"), + (0x021E, "\u{21f}"), + (0x0220, "\u{19e}"), + (0x0222, "\u{223}"), + (0x0224, "\u{225}"), + (0x0226, "\u{227}"), + (0x0228, "\u{229}"), + (0x022A, "\u{22b}"), + (0x022C, "\u{22d}"), + (0x022E, "\u{22f}"), + (0x0230, "\u{231}"), + (0x0232, "\u{233}"), + (0x023A, "\u{2c65}"), + (0x023B, "\u{23c}"), + (0x023D, "\u{19a}"), + (0x023E, "\u{2c66}"), + (0x0241, "\u{242}"), + (0x0243, "\u{180}"), + (0x0244, "\u{289}"), + (0x0245, "\u{28c}"), + (0x0246, "\u{247}"), + (0x0248, "\u{249}"), + (0x024A, "\u{24b}"), + (0x024C, "\u{24d}"), + (0x024E, "\u{24f}"), + (0x0345, "\u{3b9}"), + (0x0370, "\u{371}"), + (0x0372, "\u{373}"), + (0x0376, "\u{377}"), + (0x037F, "\u{3f3}"), + (0x0386, "\u{3ac}"), + (0x0388, "\u{3ad}"), + (0x0389, "\u{3ae}"), + (0x038A, "\u{3af}"), + (0x038C, "\u{3cc}"), + (0x038E, "\u{3cd}"), + (0x038F, "\u{3ce}"), + (0x0390, "\u{3b9}\u{308}\u{301}"), + (0x0391, "\u{3b1}"), + (0x0392, "\u{3b2}"), + (0x0393, "\u{3b3}"), + (0x0394, "\u{3b4}"), + (0x0395, "\u{3b5}"), + (0x0396, "\u{3b6}"), + (0x0397, "\u{3b7}"), + (0x0398, "\u{3b8}"), + (0x0399, "\u{3b9}"), + (0x039A, "\u{3ba}"), + (0x039B, "\u{3bb}"), + (0x039C, "\u{3bc}"), + (0x039D, "\u{3bd}"), + (0x039E, "\u{3be}"), + (0x039F, "\u{3bf}"), + (0x03A0, "\u{3c0}"), + (0x03A1, "\u{3c1}"), + (0x03A3, "\u{3c3}"), + (0x03A4, "\u{3c4}"), + (0x03A5, "\u{3c5}"), + (0x03A6, "\u{3c6}"), + (0x03A7, "\u{3c7}"), + (0x03A8, "\u{3c8}"), + (0x03A9, "\u{3c9}"), + (0x03AA, "\u{3ca}"), + (0x03AB, "\u{3cb}"), + (0x03B0, "\u{3c5}\u{308}\u{301}"), + (0x03C2, "\u{3c3}"), + (0x03CF, "\u{3d7}"), + (0x03D0, "\u{3b2}"), + (0x03D1, "\u{3b8}"), + (0x03D5, "\u{3c6}"), + (0x03D6, "\u{3c0}"), + (0x03D8, "\u{3d9}"), + (0x03DA, "\u{3db}"), + (0x03DC, "\u{3dd}"), + (0x03DE, "\u{3df}"), + (0x03E0, "\u{3e1}"), + (0x03E2, "\u{3e3}"), + (0x03E4, "\u{3e5}"), + (0x03E6, "\u{3e7}"), + (0x03E8, "\u{3e9}"), + (0x03EA, "\u{3eb}"), + (0x03EC, "\u{3ed}"), + (0x03EE, "\u{3ef}"), + (0x03F0, "\u{3ba}"), + (0x03F1, "\u{3c1}"), + (0x03F4, "\u{3b8}"), + (0x03F5, "\u{3b5}"), + (0x03F7, "\u{3f8}"), + (0x03F9, "\u{3f2}"), + (0x03FA, "\u{3fb}"), + (0x03FD, "\u{37b}"), + (0x03FE, "\u{37c}"), + (0x03FF, "\u{37d}"), + (0x0400, "\u{450}"), + (0x0401, "\u{451}"), + (0x0402, "\u{452}"), + (0x0403, "\u{453}"), + (0x0404, "\u{454}"), + (0x0405, "\u{455}"), + (0x0406, "\u{456}"), + (0x0407, "\u{457}"), + (0x0408, "\u{458}"), + (0x0409, "\u{459}"), + (0x040A, "\u{45a}"), + (0x040B, "\u{45b}"), + (0x040C, "\u{45c}"), + (0x040D, "\u{45d}"), + (0x040E, "\u{45e}"), + (0x040F, "\u{45f}"), + (0x0410, "\u{430}"), + (0x0411, "\u{431}"), + (0x0412, "\u{432}"), + (0x0413, "\u{433}"), + (0x0414, "\u{434}"), + (0x0415, "\u{435}"), + (0x0416, "\u{436}"), + (0x0417, "\u{437}"), + (0x0418, "\u{438}"), + (0x0419, "\u{439}"), + (0x041A, "\u{43a}"), + (0x041B, "\u{43b}"), + (0x041C, "\u{43c}"), + (0x041D, "\u{43d}"), + (0x041E, "\u{43e}"), + (0x041F, "\u{43f}"), + (0x0420, "\u{440}"), + (0x0421, "\u{441}"), + (0x0422, "\u{442}"), + (0x0423, "\u{443}"), + (0x0424, "\u{444}"), + (0x0425, "\u{445}"), + (0x0426, "\u{446}"), + (0x0427, "\u{447}"), + (0x0428, "\u{448}"), + (0x0429, "\u{449}"), + (0x042A, "\u{44a}"), + (0x042B, "\u{44b}"), + (0x042C, "\u{44c}"), + (0x042D, "\u{44d}"), + (0x042E, "\u{44e}"), + (0x042F, "\u{44f}"), + (0x0460, "\u{461}"), + (0x0462, "\u{463}"), + (0x0464, "\u{465}"), + (0x0466, "\u{467}"), + (0x0468, "\u{469}"), + (0x046A, "\u{46b}"), + (0x046C, "\u{46d}"), + (0x046E, "\u{46f}"), + (0x0470, "\u{471}"), + (0x0472, "\u{473}"), + (0x0474, "\u{475}"), + (0x0476, "\u{477}"), + (0x0478, "\u{479}"), + (0x047A, "\u{47b}"), + (0x047C, "\u{47d}"), + (0x047E, "\u{47f}"), + (0x0480, "\u{481}"), + (0x048A, "\u{48b}"), + (0x048C, "\u{48d}"), + (0x048E, "\u{48f}"), + (0x0490, "\u{491}"), + (0x0492, "\u{493}"), + (0x0494, "\u{495}"), + (0x0496, "\u{497}"), + (0x0498, "\u{499}"), + (0x049A, "\u{49b}"), + (0x049C, "\u{49d}"), + (0x049E, "\u{49f}"), + (0x04A0, "\u{4a1}"), + (0x04A2, "\u{4a3}"), + (0x04A4, "\u{4a5}"), + (0x04A6, "\u{4a7}"), + (0x04A8, "\u{4a9}"), + (0x04AA, "\u{4ab}"), + (0x04AC, "\u{4ad}"), + (0x04AE, "\u{4af}"), + (0x04B0, "\u{4b1}"), + (0x04B2, "\u{4b3}"), + (0x04B4, "\u{4b5}"), + (0x04B6, "\u{4b7}"), + (0x04B8, "\u{4b9}"), + (0x04BA, "\u{4bb}"), + (0x04BC, "\u{4bd}"), + (0x04BE, "\u{4bf}"), + (0x04C0, "\u{4cf}"), + (0x04C1, "\u{4c2}"), + (0x04C3, "\u{4c4}"), + (0x04C5, "\u{4c6}"), + (0x04C7, "\u{4c8}"), + (0x04C9, "\u{4ca}"), + (0x04CB, "\u{4cc}"), + (0x04CD, "\u{4ce}"), + (0x04D0, "\u{4d1}"), + (0x04D2, "\u{4d3}"), + (0x04D4, "\u{4d5}"), + (0x04D6, "\u{4d7}"), + (0x04D8, "\u{4d9}"), + (0x04DA, "\u{4db}"), + (0x04DC, "\u{4dd}"), + (0x04DE, "\u{4df}"), + (0x04E0, "\u{4e1}"), + (0x04E2, "\u{4e3}"), + (0x04E4, "\u{4e5}"), + (0x04E6, "\u{4e7}"), + (0x04E8, "\u{4e9}"), + (0x04EA, "\u{4eb}"), + (0x04EC, "\u{4ed}"), + (0x04EE, "\u{4ef}"), + (0x04F0, "\u{4f1}"), + (0x04F2, "\u{4f3}"), + (0x04F4, "\u{4f5}"), + (0x04F6, "\u{4f7}"), + (0x04F8, "\u{4f9}"), + (0x04FA, "\u{4fb}"), + (0x04FC, "\u{4fd}"), + (0x04FE, "\u{4ff}"), + (0x0500, "\u{501}"), + (0x0502, "\u{503}"), + (0x0504, "\u{505}"), + (0x0506, "\u{507}"), + (0x0508, "\u{509}"), + (0x050A, "\u{50b}"), + (0x050C, "\u{50d}"), + (0x050E, "\u{50f}"), + (0x0510, "\u{511}"), + (0x0512, "\u{513}"), + (0x0514, "\u{515}"), + (0x0516, "\u{517}"), + (0x0518, "\u{519}"), + (0x051A, "\u{51b}"), + (0x051C, "\u{51d}"), + (0x051E, "\u{51f}"), + (0x0520, "\u{521}"), + (0x0522, "\u{523}"), + (0x0524, "\u{525}"), + (0x0526, "\u{527}"), + (0x0528, "\u{529}"), + (0x052A, "\u{52b}"), + (0x052C, "\u{52d}"), + (0x052E, "\u{52f}"), + (0x0531, "\u{561}"), + (0x0532, "\u{562}"), + (0x0533, "\u{563}"), + (0x0534, "\u{564}"), + (0x0535, "\u{565}"), + (0x0536, "\u{566}"), + (0x0537, "\u{567}"), + (0x0538, "\u{568}"), + (0x0539, "\u{569}"), + (0x053A, "\u{56a}"), + (0x053B, "\u{56b}"), + (0x053C, "\u{56c}"), + (0x053D, "\u{56d}"), + (0x053E, "\u{56e}"), + (0x053F, "\u{56f}"), + (0x0540, "\u{570}"), + (0x0541, "\u{571}"), + (0x0542, "\u{572}"), + (0x0543, "\u{573}"), + (0x0544, "\u{574}"), + (0x0545, "\u{575}"), + (0x0546, "\u{576}"), + (0x0547, "\u{577}"), + (0x0548, "\u{578}"), + (0x0549, "\u{579}"), + (0x054A, "\u{57a}"), + (0x054B, "\u{57b}"), + (0x054C, "\u{57c}"), + (0x054D, "\u{57d}"), + (0x054E, "\u{57e}"), + (0x054F, "\u{57f}"), + (0x0550, "\u{580}"), + (0x0551, "\u{581}"), + (0x0552, "\u{582}"), + (0x0553, "\u{583}"), + (0x0554, "\u{584}"), + (0x0555, "\u{585}"), + (0x0556, "\u{586}"), + (0x0587, "\u{565}\u{582}"), + (0x10A0, "\u{2d00}"), + (0x10A1, "\u{2d01}"), + (0x10A2, "\u{2d02}"), + (0x10A3, "\u{2d03}"), + (0x10A4, "\u{2d04}"), + (0x10A5, "\u{2d05}"), + (0x10A6, "\u{2d06}"), + (0x10A7, "\u{2d07}"), + (0x10A8, "\u{2d08}"), + (0x10A9, "\u{2d09}"), + (0x10AA, "\u{2d0a}"), + (0x10AB, "\u{2d0b}"), + (0x10AC, "\u{2d0c}"), + (0x10AD, "\u{2d0d}"), + (0x10AE, "\u{2d0e}"), + (0x10AF, "\u{2d0f}"), + (0x10B0, "\u{2d10}"), + (0x10B1, "\u{2d11}"), + (0x10B2, "\u{2d12}"), + (0x10B3, "\u{2d13}"), + (0x10B4, "\u{2d14}"), + (0x10B5, "\u{2d15}"), + (0x10B6, "\u{2d16}"), + (0x10B7, "\u{2d17}"), + (0x10B8, "\u{2d18}"), + (0x10B9, "\u{2d19}"), + (0x10BA, "\u{2d1a}"), + (0x10BB, "\u{2d1b}"), + (0x10BC, "\u{2d1c}"), + (0x10BD, "\u{2d1d}"), + (0x10BE, "\u{2d1e}"), + (0x10BF, "\u{2d1f}"), + (0x10C0, "\u{2d20}"), + (0x10C1, "\u{2d21}"), + (0x10C2, "\u{2d22}"), + (0x10C3, "\u{2d23}"), + (0x10C4, "\u{2d24}"), + (0x10C5, "\u{2d25}"), + (0x10C7, "\u{2d27}"), + (0x10CD, "\u{2d2d}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{432}"), + (0x1C81, "\u{434}"), + (0x1C82, "\u{43e}"), + (0x1C83, "\u{441}"), + (0x1C84, "\u{442}"), + (0x1C85, "\u{442}"), + (0x1C86, "\u{44a}"), + (0x1C87, "\u{463}"), + (0x1C88, "\u{a64b}"), + (0x1C90, "\u{10d0}"), + (0x1C91, "\u{10d1}"), + (0x1C92, "\u{10d2}"), + (0x1C93, "\u{10d3}"), + (0x1C94, "\u{10d4}"), + (0x1C95, "\u{10d5}"), + (0x1C96, "\u{10d6}"), + (0x1C97, "\u{10d7}"), + (0x1C98, "\u{10d8}"), + (0x1C99, "\u{10d9}"), + (0x1C9A, "\u{10da}"), + (0x1C9B, "\u{10db}"), + (0x1C9C, "\u{10dc}"), + (0x1C9D, "\u{10dd}"), + (0x1C9E, "\u{10de}"), + (0x1C9F, "\u{10df}"), + (0x1CA0, "\u{10e0}"), + (0x1CA1, "\u{10e1}"), + (0x1CA2, "\u{10e2}"), + (0x1CA3, "\u{10e3}"), + (0x1CA4, "\u{10e4}"), + (0x1CA5, "\u{10e5}"), + (0x1CA6, "\u{10e6}"), + (0x1CA7, "\u{10e7}"), + (0x1CA8, "\u{10e8}"), + (0x1CA9, "\u{10e9}"), + (0x1CAA, "\u{10ea}"), + (0x1CAB, "\u{10eb}"), + (0x1CAC, "\u{10ec}"), + (0x1CAD, "\u{10ed}"), + (0x1CAE, "\u{10ee}"), + (0x1CAF, "\u{10ef}"), + (0x1CB0, "\u{10f0}"), + (0x1CB1, "\u{10f1}"), + (0x1CB2, "\u{10f2}"), + (0x1CB3, "\u{10f3}"), + (0x1CB4, "\u{10f4}"), + (0x1CB5, "\u{10f5}"), + (0x1CB6, "\u{10f6}"), + (0x1CB7, "\u{10f7}"), + (0x1CB8, "\u{10f8}"), + (0x1CB9, "\u{10f9}"), + (0x1CBA, "\u{10fa}"), + (0x1CBD, "\u{10fd}"), + (0x1CBE, "\u{10fe}"), + (0x1CBF, "\u{10ff}"), + (0x1E00, "\u{1e01}"), + (0x1E02, "\u{1e03}"), + (0x1E04, "\u{1e05}"), + (0x1E06, "\u{1e07}"), + (0x1E08, "\u{1e09}"), + (0x1E0A, "\u{1e0b}"), + (0x1E0C, "\u{1e0d}"), + (0x1E0E, "\u{1e0f}"), + (0x1E10, "\u{1e11}"), + (0x1E12, "\u{1e13}"), + (0x1E14, "\u{1e15}"), + (0x1E16, "\u{1e17}"), + (0x1E18, "\u{1e19}"), + (0x1E1A, "\u{1e1b}"), + (0x1E1C, "\u{1e1d}"), + (0x1E1E, "\u{1e1f}"), + (0x1E20, "\u{1e21}"), + (0x1E22, "\u{1e23}"), + (0x1E24, "\u{1e25}"), + (0x1E26, "\u{1e27}"), + (0x1E28, "\u{1e29}"), + (0x1E2A, "\u{1e2b}"), + (0x1E2C, "\u{1e2d}"), + (0x1E2E, "\u{1e2f}"), + (0x1E30, "\u{1e31}"), + (0x1E32, "\u{1e33}"), + (0x1E34, "\u{1e35}"), + (0x1E36, "\u{1e37}"), + (0x1E38, "\u{1e39}"), + (0x1E3A, "\u{1e3b}"), + (0x1E3C, "\u{1e3d}"), + (0x1E3E, "\u{1e3f}"), + (0x1E40, "\u{1e41}"), + (0x1E42, "\u{1e43}"), + (0x1E44, "\u{1e45}"), + (0x1E46, "\u{1e47}"), + (0x1E48, "\u{1e49}"), + (0x1E4A, "\u{1e4b}"), + (0x1E4C, "\u{1e4d}"), + (0x1E4E, "\u{1e4f}"), + (0x1E50, "\u{1e51}"), + (0x1E52, "\u{1e53}"), + (0x1E54, "\u{1e55}"), + (0x1E56, "\u{1e57}"), + (0x1E58, "\u{1e59}"), + (0x1E5A, "\u{1e5b}"), + (0x1E5C, "\u{1e5d}"), + (0x1E5E, "\u{1e5f}"), + (0x1E60, "\u{1e61}"), + (0x1E62, "\u{1e63}"), + (0x1E64, "\u{1e65}"), + (0x1E66, "\u{1e67}"), + (0x1E68, "\u{1e69}"), + (0x1E6A, "\u{1e6b}"), + (0x1E6C, "\u{1e6d}"), + (0x1E6E, "\u{1e6f}"), + (0x1E70, "\u{1e71}"), + (0x1E72, "\u{1e73}"), + (0x1E74, "\u{1e75}"), + (0x1E76, "\u{1e77}"), + (0x1E78, "\u{1e79}"), + (0x1E7A, "\u{1e7b}"), + (0x1E7C, "\u{1e7d}"), + (0x1E7E, "\u{1e7f}"), + (0x1E80, "\u{1e81}"), + (0x1E82, "\u{1e83}"), + (0x1E84, "\u{1e85}"), + (0x1E86, "\u{1e87}"), + (0x1E88, "\u{1e89}"), + (0x1E8A, "\u{1e8b}"), + (0x1E8C, "\u{1e8d}"), + (0x1E8E, "\u{1e8f}"), + (0x1E90, "\u{1e91}"), + (0x1E92, "\u{1e93}"), + (0x1E94, "\u{1e95}"), + (0x1E96, "\u{68}\u{331}"), + (0x1E97, "\u{74}\u{308}"), + (0x1E98, "\u{77}\u{30a}"), + (0x1E99, "\u{79}\u{30a}"), + (0x1E9A, "\u{61}\u{2be}"), + (0x1E9B, "\u{1e61}"), + (0x1E9E, "\u{73}\u{73}"), + (0x1EA0, "\u{1ea1}"), + (0x1EA2, "\u{1ea3}"), + (0x1EA4, "\u{1ea5}"), + (0x1EA6, "\u{1ea7}"), + (0x1EA8, "\u{1ea9}"), + (0x1EAA, "\u{1eab}"), + (0x1EAC, "\u{1ead}"), + (0x1EAE, "\u{1eaf}"), + (0x1EB0, "\u{1eb1}"), + (0x1EB2, "\u{1eb3}"), + (0x1EB4, "\u{1eb5}"), + (0x1EB6, "\u{1eb7}"), + (0x1EB8, "\u{1eb9}"), + (0x1EBA, "\u{1ebb}"), + (0x1EBC, "\u{1ebd}"), + (0x1EBE, "\u{1ebf}"), + (0x1EC0, "\u{1ec1}"), + (0x1EC2, "\u{1ec3}"), + (0x1EC4, "\u{1ec5}"), + (0x1EC6, "\u{1ec7}"), + (0x1EC8, "\u{1ec9}"), + (0x1ECA, "\u{1ecb}"), + (0x1ECC, "\u{1ecd}"), + (0x1ECE, "\u{1ecf}"), + (0x1ED0, "\u{1ed1}"), + (0x1ED2, "\u{1ed3}"), + (0x1ED4, "\u{1ed5}"), + (0x1ED6, "\u{1ed7}"), + (0x1ED8, "\u{1ed9}"), + (0x1EDA, "\u{1edb}"), + (0x1EDC, "\u{1edd}"), + (0x1EDE, "\u{1edf}"), + (0x1EE0, "\u{1ee1}"), + (0x1EE2, "\u{1ee3}"), + (0x1EE4, "\u{1ee5}"), + (0x1EE6, "\u{1ee7}"), + (0x1EE8, "\u{1ee9}"), + (0x1EEA, "\u{1eeb}"), + (0x1EEC, "\u{1eed}"), + (0x1EEE, "\u{1eef}"), + (0x1EF0, "\u{1ef1}"), + (0x1EF2, "\u{1ef3}"), + (0x1EF4, "\u{1ef5}"), + (0x1EF6, "\u{1ef7}"), + (0x1EF8, "\u{1ef9}"), + (0x1EFA, "\u{1efb}"), + (0x1EFC, "\u{1efd}"), + (0x1EFE, "\u{1eff}"), + (0x1F08, "\u{1f00}"), + (0x1F09, "\u{1f01}"), + (0x1F0A, "\u{1f02}"), + (0x1F0B, "\u{1f03}"), + (0x1F0C, "\u{1f04}"), + (0x1F0D, "\u{1f05}"), + (0x1F0E, "\u{1f06}"), + (0x1F0F, "\u{1f07}"), + (0x1F18, "\u{1f10}"), + (0x1F19, "\u{1f11}"), + (0x1F1A, "\u{1f12}"), + (0x1F1B, "\u{1f13}"), + (0x1F1C, "\u{1f14}"), + (0x1F1D, "\u{1f15}"), + (0x1F28, "\u{1f20}"), + (0x1F29, "\u{1f21}"), + (0x1F2A, "\u{1f22}"), + (0x1F2B, "\u{1f23}"), + (0x1F2C, "\u{1f24}"), + (0x1F2D, "\u{1f25}"), + (0x1F2E, "\u{1f26}"), + (0x1F2F, "\u{1f27}"), + (0x1F38, "\u{1f30}"), + (0x1F39, "\u{1f31}"), + (0x1F3A, "\u{1f32}"), + (0x1F3B, "\u{1f33}"), + (0x1F3C, "\u{1f34}"), + (0x1F3D, "\u{1f35}"), + (0x1F3E, "\u{1f36}"), + (0x1F3F, "\u{1f37}"), + (0x1F48, "\u{1f40}"), + (0x1F49, "\u{1f41}"), + (0x1F4A, "\u{1f42}"), + (0x1F4B, "\u{1f43}"), + (0x1F4C, "\u{1f44}"), + (0x1F4D, "\u{1f45}"), + (0x1F50, "\u{3c5}\u{313}"), + (0x1F52, "\u{3c5}\u{313}\u{300}"), + (0x1F54, "\u{3c5}\u{313}\u{301}"), + (0x1F56, "\u{3c5}\u{313}\u{342}"), + (0x1F59, "\u{1f51}"), + (0x1F5B, "\u{1f53}"), + (0x1F5D, "\u{1f55}"), + (0x1F5F, "\u{1f57}"), + (0x1F68, "\u{1f60}"), + (0x1F69, "\u{1f61}"), + (0x1F6A, "\u{1f62}"), + (0x1F6B, "\u{1f63}"), + (0x1F6C, "\u{1f64}"), + (0x1F6D, "\u{1f65}"), + (0x1F6E, "\u{1f66}"), + (0x1F6F, "\u{1f67}"), + (0x1F80, "\u{1f00}\u{3b9}"), + (0x1F81, "\u{1f01}\u{3b9}"), + (0x1F82, "\u{1f02}\u{3b9}"), + (0x1F83, "\u{1f03}\u{3b9}"), + (0x1F84, "\u{1f04}\u{3b9}"), + (0x1F85, "\u{1f05}\u{3b9}"), + (0x1F86, "\u{1f06}\u{3b9}"), + (0x1F87, "\u{1f07}\u{3b9}"), + (0x1F88, "\u{1f00}\u{3b9}"), + (0x1F89, "\u{1f01}\u{3b9}"), + (0x1F8A, "\u{1f02}\u{3b9}"), + (0x1F8B, "\u{1f03}\u{3b9}"), + (0x1F8C, "\u{1f04}\u{3b9}"), + (0x1F8D, "\u{1f05}\u{3b9}"), + (0x1F8E, "\u{1f06}\u{3b9}"), + (0x1F8F, "\u{1f07}\u{3b9}"), + (0x1F90, "\u{1f20}\u{3b9}"), + (0x1F91, "\u{1f21}\u{3b9}"), + (0x1F92, "\u{1f22}\u{3b9}"), + (0x1F93, "\u{1f23}\u{3b9}"), + (0x1F94, "\u{1f24}\u{3b9}"), + (0x1F95, "\u{1f25}\u{3b9}"), + (0x1F96, "\u{1f26}\u{3b9}"), + (0x1F97, "\u{1f27}\u{3b9}"), + (0x1F98, "\u{1f20}\u{3b9}"), + (0x1F99, "\u{1f21}\u{3b9}"), + (0x1F9A, "\u{1f22}\u{3b9}"), + (0x1F9B, "\u{1f23}\u{3b9}"), + (0x1F9C, "\u{1f24}\u{3b9}"), + (0x1F9D, "\u{1f25}\u{3b9}"), + (0x1F9E, "\u{1f26}\u{3b9}"), + (0x1F9F, "\u{1f27}\u{3b9}"), + (0x1FA0, "\u{1f60}\u{3b9}"), + (0x1FA1, "\u{1f61}\u{3b9}"), + (0x1FA2, "\u{1f62}\u{3b9}"), + (0x1FA3, "\u{1f63}\u{3b9}"), + (0x1FA4, "\u{1f64}\u{3b9}"), + (0x1FA5, "\u{1f65}\u{3b9}"), + (0x1FA6, "\u{1f66}\u{3b9}"), + (0x1FA7, "\u{1f67}\u{3b9}"), + (0x1FA8, "\u{1f60}\u{3b9}"), + (0x1FA9, "\u{1f61}\u{3b9}"), + (0x1FAA, "\u{1f62}\u{3b9}"), + (0x1FAB, "\u{1f63}\u{3b9}"), + (0x1FAC, "\u{1f64}\u{3b9}"), + (0x1FAD, "\u{1f65}\u{3b9}"), + (0x1FAE, "\u{1f66}\u{3b9}"), + (0x1FAF, "\u{1f67}\u{3b9}"), + (0x1FB2, "\u{1f70}\u{3b9}"), + (0x1FB3, "\u{3b1}\u{3b9}"), + (0x1FB4, "\u{3ac}\u{3b9}"), + (0x1FB6, "\u{3b1}\u{342}"), + (0x1FB7, "\u{3b1}\u{342}\u{3b9}"), + (0x1FB8, "\u{1fb0}"), + (0x1FB9, "\u{1fb1}"), + (0x1FBA, "\u{1f70}"), + (0x1FBB, "\u{1f71}"), + (0x1FBC, "\u{3b1}\u{3b9}"), + (0x1FBE, "\u{3b9}"), + (0x1FC2, "\u{1f74}\u{3b9}"), + (0x1FC3, "\u{3b7}\u{3b9}"), + (0x1FC4, "\u{3ae}\u{3b9}"), + (0x1FC6, "\u{3b7}\u{342}"), + (0x1FC7, "\u{3b7}\u{342}\u{3b9}"), + (0x1FC8, "\u{1f72}"), + (0x1FC9, "\u{1f73}"), + (0x1FCA, "\u{1f74}"), + (0x1FCB, "\u{1f75}"), + (0x1FCC, "\u{3b7}\u{3b9}"), + (0x1FD2, "\u{3b9}\u{308}\u{300}"), + (0x1FD3, "\u{3b9}\u{308}\u{301}"), + (0x1FD6, "\u{3b9}\u{342}"), + (0x1FD7, "\u{3b9}\u{308}\u{342}"), + (0x1FD8, "\u{1fd0}"), + (0x1FD9, "\u{1fd1}"), + (0x1FDA, "\u{1f76}"), + (0x1FDB, "\u{1f77}"), + (0x1FE2, "\u{3c5}\u{308}\u{300}"), + (0x1FE3, "\u{3c5}\u{308}\u{301}"), + (0x1FE4, "\u{3c1}\u{313}"), + (0x1FE6, "\u{3c5}\u{342}"), + (0x1FE7, "\u{3c5}\u{308}\u{342}"), + (0x1FE8, "\u{1fe0}"), + (0x1FE9, "\u{1fe1}"), + (0x1FEA, "\u{1f7a}"), + (0x1FEB, "\u{1f7b}"), + (0x1FEC, "\u{1fe5}"), + (0x1FF2, "\u{1f7c}\u{3b9}"), + (0x1FF3, "\u{3c9}\u{3b9}"), + (0x1FF4, "\u{3ce}\u{3b9}"), + (0x1FF6, "\u{3c9}\u{342}"), + (0x1FF7, "\u{3c9}\u{342}\u{3b9}"), + (0x1FF8, "\u{1f78}"), + (0x1FF9, "\u{1f79}"), + (0x1FFA, "\u{1f7c}"), + (0x1FFB, "\u{1f7d}"), + (0x1FFC, "\u{3c9}\u{3b9}"), + (0x2126, "\u{3c9}"), + (0x212A, "\u{6b}"), + (0x212B, "\u{e5}"), + (0x2132, "\u{214e}"), + (0x2160, "\u{2170}"), + (0x2161, "\u{2171}"), + (0x2162, "\u{2172}"), + (0x2163, "\u{2173}"), + (0x2164, "\u{2174}"), + (0x2165, "\u{2175}"), + (0x2166, "\u{2176}"), + (0x2167, "\u{2177}"), + (0x2168, "\u{2178}"), + (0x2169, "\u{2179}"), + (0x216A, "\u{217a}"), + (0x216B, "\u{217b}"), + (0x216C, "\u{217c}"), + (0x216D, "\u{217d}"), + (0x216E, "\u{217e}"), + (0x216F, "\u{217f}"), + (0x2183, "\u{2184}"), + (0x24B6, "\u{24d0}"), + (0x24B7, "\u{24d1}"), + (0x24B8, "\u{24d2}"), + (0x24B9, "\u{24d3}"), + (0x24BA, "\u{24d4}"), + (0x24BB, "\u{24d5}"), + (0x24BC, "\u{24d6}"), + (0x24BD, "\u{24d7}"), + (0x24BE, "\u{24d8}"), + (0x24BF, "\u{24d9}"), + (0x24C0, "\u{24da}"), + (0x24C1, "\u{24db}"), + (0x24C2, "\u{24dc}"), + (0x24C3, "\u{24dd}"), + (0x24C4, "\u{24de}"), + (0x24C5, "\u{24df}"), + (0x24C6, "\u{24e0}"), + (0x24C7, "\u{24e1}"), + (0x24C8, "\u{24e2}"), + (0x24C9, "\u{24e3}"), + (0x24CA, "\u{24e4}"), + (0x24CB, "\u{24e5}"), + (0x24CC, "\u{24e6}"), + (0x24CD, "\u{24e7}"), + (0x24CE, "\u{24e8}"), + (0x24CF, "\u{24e9}"), + (0x2C00, "\u{2c30}"), + (0x2C01, "\u{2c31}"), + (0x2C02, "\u{2c32}"), + (0x2C03, "\u{2c33}"), + (0x2C04, "\u{2c34}"), + (0x2C05, "\u{2c35}"), + (0x2C06, "\u{2c36}"), + (0x2C07, "\u{2c37}"), + (0x2C08, "\u{2c38}"), + (0x2C09, "\u{2c39}"), + (0x2C0A, "\u{2c3a}"), + (0x2C0B, "\u{2c3b}"), + (0x2C0C, "\u{2c3c}"), + (0x2C0D, "\u{2c3d}"), + (0x2C0E, "\u{2c3e}"), + (0x2C0F, "\u{2c3f}"), + (0x2C10, "\u{2c40}"), + (0x2C11, "\u{2c41}"), + (0x2C12, "\u{2c42}"), + (0x2C13, "\u{2c43}"), + (0x2C14, "\u{2c44}"), + (0x2C15, "\u{2c45}"), + (0x2C16, "\u{2c46}"), + (0x2C17, "\u{2c47}"), + (0x2C18, "\u{2c48}"), + (0x2C19, "\u{2c49}"), + (0x2C1A, "\u{2c4a}"), + (0x2C1B, "\u{2c4b}"), + (0x2C1C, "\u{2c4c}"), + (0x2C1D, "\u{2c4d}"), + (0x2C1E, "\u{2c4e}"), + (0x2C1F, "\u{2c4f}"), + (0x2C20, "\u{2c50}"), + (0x2C21, "\u{2c51}"), + (0x2C22, "\u{2c52}"), + (0x2C23, "\u{2c53}"), + (0x2C24, "\u{2c54}"), + (0x2C25, "\u{2c55}"), + (0x2C26, "\u{2c56}"), + (0x2C27, "\u{2c57}"), + (0x2C28, "\u{2c58}"), + (0x2C29, "\u{2c59}"), + (0x2C2A, "\u{2c5a}"), + (0x2C2B, "\u{2c5b}"), + (0x2C2C, "\u{2c5c}"), + (0x2C2D, "\u{2c5d}"), + (0x2C2E, "\u{2c5e}"), + (0x2C2F, "\u{2c5f}"), + (0x2C60, "\u{2c61}"), + (0x2C62, "\u{26b}"), + (0x2C63, "\u{1d7d}"), + (0x2C64, "\u{27d}"), + (0x2C67, "\u{2c68}"), + (0x2C69, "\u{2c6a}"), + (0x2C6B, "\u{2c6c}"), + (0x2C6D, "\u{251}"), + (0x2C6E, "\u{271}"), + (0x2C6F, "\u{250}"), + (0x2C70, "\u{252}"), + (0x2C72, "\u{2c73}"), + (0x2C75, "\u{2c76}"), + (0x2C7E, "\u{23f}"), + (0x2C7F, "\u{240}"), + (0x2C80, "\u{2c81}"), + (0x2C82, "\u{2c83}"), + (0x2C84, "\u{2c85}"), + (0x2C86, "\u{2c87}"), + (0x2C88, "\u{2c89}"), + (0x2C8A, "\u{2c8b}"), + (0x2C8C, "\u{2c8d}"), + (0x2C8E, "\u{2c8f}"), + (0x2C90, "\u{2c91}"), + (0x2C92, "\u{2c93}"), + (0x2C94, "\u{2c95}"), + (0x2C96, "\u{2c97}"), + (0x2C98, "\u{2c99}"), + (0x2C9A, "\u{2c9b}"), + (0x2C9C, "\u{2c9d}"), + (0x2C9E, "\u{2c9f}"), + (0x2CA0, "\u{2ca1}"), + (0x2CA2, "\u{2ca3}"), + (0x2CA4, "\u{2ca5}"), + (0x2CA6, "\u{2ca7}"), + (0x2CA8, "\u{2ca9}"), + (0x2CAA, "\u{2cab}"), + (0x2CAC, "\u{2cad}"), + (0x2CAE, "\u{2caf}"), + (0x2CB0, "\u{2cb1}"), + (0x2CB2, "\u{2cb3}"), + (0x2CB4, "\u{2cb5}"), + (0x2CB6, "\u{2cb7}"), + (0x2CB8, "\u{2cb9}"), + (0x2CBA, "\u{2cbb}"), + (0x2CBC, "\u{2cbd}"), + (0x2CBE, "\u{2cbf}"), + (0x2CC0, "\u{2cc1}"), + (0x2CC2, "\u{2cc3}"), + (0x2CC4, "\u{2cc5}"), + (0x2CC6, "\u{2cc7}"), + (0x2CC8, "\u{2cc9}"), + (0x2CCA, "\u{2ccb}"), + (0x2CCC, "\u{2ccd}"), + (0x2CCE, "\u{2ccf}"), + (0x2CD0, "\u{2cd1}"), + (0x2CD2, "\u{2cd3}"), + (0x2CD4, "\u{2cd5}"), + (0x2CD6, "\u{2cd7}"), + (0x2CD8, "\u{2cd9}"), + (0x2CDA, "\u{2cdb}"), + (0x2CDC, "\u{2cdd}"), + (0x2CDE, "\u{2cdf}"), + (0x2CE0, "\u{2ce1}"), + (0x2CE2, "\u{2ce3}"), + (0x2CEB, "\u{2cec}"), + (0x2CED, "\u{2cee}"), + (0x2CF2, "\u{2cf3}"), + (0xA640, "\u{a641}"), + (0xA642, "\u{a643}"), + (0xA644, "\u{a645}"), + (0xA646, "\u{a647}"), + (0xA648, "\u{a649}"), + (0xA64A, "\u{a64b}"), + (0xA64C, "\u{a64d}"), + (0xA64E, "\u{a64f}"), + (0xA650, "\u{a651}"), + (0xA652, "\u{a653}"), + (0xA654, "\u{a655}"), + (0xA656, "\u{a657}"), + (0xA658, "\u{a659}"), + (0xA65A, "\u{a65b}"), + (0xA65C, "\u{a65d}"), + (0xA65E, "\u{a65f}"), + (0xA660, "\u{a661}"), + (0xA662, "\u{a663}"), + (0xA664, "\u{a665}"), + (0xA666, "\u{a667}"), + (0xA668, "\u{a669}"), + (0xA66A, "\u{a66b}"), + (0xA66C, "\u{a66d}"), + (0xA680, "\u{a681}"), + (0xA682, "\u{a683}"), + (0xA684, "\u{a685}"), + (0xA686, "\u{a687}"), + (0xA688, "\u{a689}"), + (0xA68A, "\u{a68b}"), + (0xA68C, "\u{a68d}"), + (0xA68E, "\u{a68f}"), + (0xA690, "\u{a691}"), + (0xA692, "\u{a693}"), + (0xA694, "\u{a695}"), + (0xA696, "\u{a697}"), + (0xA698, "\u{a699}"), + (0xA69A, "\u{a69b}"), + (0xA722, "\u{a723}"), + (0xA724, "\u{a725}"), + (0xA726, "\u{a727}"), + (0xA728, "\u{a729}"), + (0xA72A, "\u{a72b}"), + (0xA72C, "\u{a72d}"), + (0xA72E, "\u{a72f}"), + (0xA732, "\u{a733}"), + (0xA734, "\u{a735}"), + (0xA736, "\u{a737}"), + (0xA738, "\u{a739}"), + (0xA73A, "\u{a73b}"), + (0xA73C, "\u{a73d}"), + (0xA73E, "\u{a73f}"), + (0xA740, "\u{a741}"), + (0xA742, "\u{a743}"), + (0xA744, "\u{a745}"), + (0xA746, "\u{a747}"), + (0xA748, "\u{a749}"), + (0xA74A, "\u{a74b}"), + (0xA74C, "\u{a74d}"), + (0xA74E, "\u{a74f}"), + (0xA750, "\u{a751}"), + (0xA752, "\u{a753}"), + (0xA754, "\u{a755}"), + (0xA756, "\u{a757}"), + (0xA758, "\u{a759}"), + (0xA75A, "\u{a75b}"), + (0xA75C, "\u{a75d}"), + (0xA75E, "\u{a75f}"), + (0xA760, "\u{a761}"), + (0xA762, "\u{a763}"), + (0xA764, "\u{a765}"), + (0xA766, "\u{a767}"), + (0xA768, "\u{a769}"), + (0xA76A, "\u{a76b}"), + (0xA76C, "\u{a76d}"), + (0xA76E, "\u{a76f}"), + (0xA779, "\u{a77a}"), + (0xA77B, "\u{a77c}"), + (0xA77D, "\u{1d79}"), + (0xA77E, "\u{a77f}"), + (0xA780, "\u{a781}"), + (0xA782, "\u{a783}"), + (0xA784, "\u{a785}"), + (0xA786, "\u{a787}"), + (0xA78B, "\u{a78c}"), + (0xA78D, "\u{265}"), + (0xA790, "\u{a791}"), + (0xA792, "\u{a793}"), + (0xA796, "\u{a797}"), + (0xA798, "\u{a799}"), + (0xA79A, "\u{a79b}"), + (0xA79C, "\u{a79d}"), + (0xA79E, "\u{a79f}"), + (0xA7A0, "\u{a7a1}"), + (0xA7A2, "\u{a7a3}"), + (0xA7A4, "\u{a7a5}"), + (0xA7A6, "\u{a7a7}"), + (0xA7A8, "\u{a7a9}"), + (0xA7AA, "\u{266}"), + (0xA7AB, "\u{25c}"), + (0xA7AC, "\u{261}"), + (0xA7AD, "\u{26c}"), + (0xA7AE, "\u{26a}"), + (0xA7B0, "\u{29e}"), + (0xA7B1, "\u{287}"), + (0xA7B2, "\u{29d}"), + (0xA7B3, "\u{ab53}"), + (0xA7B4, "\u{a7b5}"), + (0xA7B6, "\u{a7b7}"), + (0xA7B8, "\u{a7b9}"), + (0xA7BA, "\u{a7bb}"), + (0xA7BC, "\u{a7bd}"), + (0xA7BE, "\u{a7bf}"), + (0xA7C0, "\u{a7c1}"), + (0xA7C2, "\u{a7c3}"), + (0xA7C4, "\u{a794}"), + (0xA7C5, "\u{282}"), + (0xA7C6, "\u{1d8e}"), + (0xA7C7, "\u{a7c8}"), + (0xA7C9, "\u{a7ca}"), + (0xA7D0, "\u{a7d1}"), + (0xA7D6, "\u{a7d7}"), + (0xA7D8, "\u{a7d9}"), + (0xA7F5, "\u{a7f6}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{66}\u{66}"), + (0xFB01, "\u{66}\u{69}"), + (0xFB02, "\u{66}\u{6c}"), + (0xFB03, "\u{66}\u{66}\u{69}"), + (0xFB04, "\u{66}\u{66}\u{6c}"), + (0xFB05, "\u{73}\u{74}"), + (0xFB06, "\u{73}\u{74}"), + (0xFB13, "\u{574}\u{576}"), + (0xFB14, "\u{574}\u{565}"), + (0xFB15, "\u{574}\u{56b}"), + (0xFB16, "\u{57e}\u{576}"), + (0xFB17, "\u{574}\u{56d}"), + (0xFF21, "\u{ff41}"), + (0xFF22, "\u{ff42}"), + (0xFF23, "\u{ff43}"), + (0xFF24, "\u{ff44}"), + (0xFF25, "\u{ff45}"), + (0xFF26, "\u{ff46}"), + (0xFF27, "\u{ff47}"), + (0xFF28, "\u{ff48}"), + (0xFF29, "\u{ff49}"), + (0xFF2A, "\u{ff4a}"), + (0xFF2B, "\u{ff4b}"), + (0xFF2C, "\u{ff4c}"), + (0xFF2D, "\u{ff4d}"), + (0xFF2E, "\u{ff4e}"), + (0xFF2F, "\u{ff4f}"), + (0xFF30, "\u{ff50}"), + (0xFF31, "\u{ff51}"), + (0xFF32, "\u{ff52}"), + (0xFF33, "\u{ff53}"), + (0xFF34, "\u{ff54}"), + (0xFF35, "\u{ff55}"), + (0xFF36, "\u{ff56}"), + (0xFF37, "\u{ff57}"), + (0xFF38, "\u{ff58}"), + (0xFF39, "\u{ff59}"), + (0xFF3A, "\u{ff5a}"), + (0x10400, "\u{10428}"), + (0x10401, "\u{10429}"), + (0x10402, "\u{1042a}"), + (0x10403, "\u{1042b}"), + (0x10404, "\u{1042c}"), + (0x10405, "\u{1042d}"), + (0x10406, "\u{1042e}"), + (0x10407, "\u{1042f}"), + (0x10408, "\u{10430}"), + (0x10409, "\u{10431}"), + (0x1040A, "\u{10432}"), + (0x1040B, "\u{10433}"), + (0x1040C, "\u{10434}"), + (0x1040D, "\u{10435}"), + (0x1040E, "\u{10436}"), + (0x1040F, "\u{10437}"), + (0x10410, "\u{10438}"), + (0x10411, "\u{10439}"), + (0x10412, "\u{1043a}"), + (0x10413, "\u{1043b}"), + (0x10414, "\u{1043c}"), + (0x10415, "\u{1043d}"), + (0x10416, "\u{1043e}"), + (0x10417, "\u{1043f}"), + (0x10418, "\u{10440}"), + (0x10419, "\u{10441}"), + (0x1041A, "\u{10442}"), + (0x1041B, "\u{10443}"), + (0x1041C, "\u{10444}"), + (0x1041D, "\u{10445}"), + (0x1041E, "\u{10446}"), + (0x1041F, "\u{10447}"), + (0x10420, "\u{10448}"), + (0x10421, "\u{10449}"), + (0x10422, "\u{1044a}"), + (0x10423, "\u{1044b}"), + (0x10424, "\u{1044c}"), + (0x10425, "\u{1044d}"), + (0x10426, "\u{1044e}"), + (0x10427, "\u{1044f}"), + (0x104B0, "\u{104d8}"), + (0x104B1, "\u{104d9}"), + (0x104B2, "\u{104da}"), + (0x104B3, "\u{104db}"), + (0x104B4, "\u{104dc}"), + (0x104B5, "\u{104dd}"), + (0x104B6, "\u{104de}"), + (0x104B7, "\u{104df}"), + (0x104B8, "\u{104e0}"), + (0x104B9, "\u{104e1}"), + (0x104BA, "\u{104e2}"), + (0x104BB, "\u{104e3}"), + (0x104BC, "\u{104e4}"), + (0x104BD, "\u{104e5}"), + (0x104BE, "\u{104e6}"), + (0x104BF, "\u{104e7}"), + (0x104C0, "\u{104e8}"), + (0x104C1, "\u{104e9}"), + (0x104C2, "\u{104ea}"), + (0x104C3, "\u{104eb}"), + (0x104C4, "\u{104ec}"), + (0x104C5, "\u{104ed}"), + (0x104C6, "\u{104ee}"), + (0x104C7, "\u{104ef}"), + (0x104C8, "\u{104f0}"), + (0x104C9, "\u{104f1}"), + (0x104CA, "\u{104f2}"), + (0x104CB, "\u{104f3}"), + (0x104CC, "\u{104f4}"), + (0x104CD, "\u{104f5}"), + (0x104CE, "\u{104f6}"), + (0x104CF, "\u{104f7}"), + (0x104D0, "\u{104f8}"), + (0x104D1, "\u{104f9}"), + (0x104D2, "\u{104fa}"), + (0x104D3, "\u{104fb}"), + (0x10570, "\u{10597}"), + (0x10571, "\u{10598}"), + (0x10572, "\u{10599}"), + (0x10573, "\u{1059a}"), + (0x10574, "\u{1059b}"), + (0x10575, "\u{1059c}"), + (0x10576, "\u{1059d}"), + (0x10577, "\u{1059e}"), + (0x10578, "\u{1059f}"), + (0x10579, "\u{105a0}"), + (0x1057A, "\u{105a1}"), + (0x1057C, "\u{105a3}"), + (0x1057D, "\u{105a4}"), + (0x1057E, "\u{105a5}"), + (0x1057F, "\u{105a6}"), + (0x10580, "\u{105a7}"), + (0x10581, "\u{105a8}"), + (0x10582, "\u{105a9}"), + (0x10583, "\u{105aa}"), + (0x10584, "\u{105ab}"), + (0x10585, "\u{105ac}"), + (0x10586, "\u{105ad}"), + (0x10587, "\u{105ae}"), + (0x10588, "\u{105af}"), + (0x10589, "\u{105b0}"), + (0x1058A, "\u{105b1}"), + (0x1058C, "\u{105b3}"), + (0x1058D, "\u{105b4}"), + (0x1058E, "\u{105b5}"), + (0x1058F, "\u{105b6}"), + (0x10590, "\u{105b7}"), + (0x10591, "\u{105b8}"), + (0x10592, "\u{105b9}"), + (0x10594, "\u{105bb}"), + (0x10595, "\u{105bc}"), + (0x10C80, "\u{10cc0}"), + (0x10C81, "\u{10cc1}"), + (0x10C82, "\u{10cc2}"), + (0x10C83, "\u{10cc3}"), + (0x10C84, "\u{10cc4}"), + (0x10C85, "\u{10cc5}"), + (0x10C86, "\u{10cc6}"), + (0x10C87, "\u{10cc7}"), + (0x10C88, "\u{10cc8}"), + (0x10C89, "\u{10cc9}"), + (0x10C8A, "\u{10cca}"), + (0x10C8B, "\u{10ccb}"), + (0x10C8C, "\u{10ccc}"), + (0x10C8D, "\u{10ccd}"), + (0x10C8E, "\u{10cce}"), + (0x10C8F, "\u{10ccf}"), + (0x10C90, "\u{10cd0}"), + (0x10C91, "\u{10cd1}"), + (0x10C92, "\u{10cd2}"), + (0x10C93, "\u{10cd3}"), + (0x10C94, "\u{10cd4}"), + (0x10C95, "\u{10cd5}"), + (0x10C96, "\u{10cd6}"), + (0x10C97, "\u{10cd7}"), + (0x10C98, "\u{10cd8}"), + (0x10C99, "\u{10cd9}"), + (0x10C9A, "\u{10cda}"), + (0x10C9B, "\u{10cdb}"), + (0x10C9C, "\u{10cdc}"), + (0x10C9D, "\u{10cdd}"), + (0x10C9E, "\u{10cde}"), + (0x10C9F, "\u{10cdf}"), + (0x10CA0, "\u{10ce0}"), + (0x10CA1, "\u{10ce1}"), + (0x10CA2, "\u{10ce2}"), + (0x10CA3, "\u{10ce3}"), + (0x10CA4, "\u{10ce4}"), + (0x10CA5, "\u{10ce5}"), + (0x10CA6, "\u{10ce6}"), + (0x10CA7, "\u{10ce7}"), + (0x10CA8, "\u{10ce8}"), + (0x10CA9, "\u{10ce9}"), + (0x10CAA, "\u{10cea}"), + (0x10CAB, "\u{10ceb}"), + (0x10CAC, "\u{10cec}"), + (0x10CAD, "\u{10ced}"), + (0x10CAE, "\u{10cee}"), + (0x10CAF, "\u{10cef}"), + (0x10CB0, "\u{10cf0}"), + (0x10CB1, "\u{10cf1}"), + (0x10CB2, "\u{10cf2}"), + (0x118A0, "\u{118c0}"), + (0x118A1, "\u{118c1}"), + (0x118A2, "\u{118c2}"), + (0x118A3, "\u{118c3}"), + (0x118A4, "\u{118c4}"), + (0x118A5, "\u{118c5}"), + (0x118A6, "\u{118c6}"), + (0x118A7, "\u{118c7}"), + (0x118A8, "\u{118c8}"), + (0x118A9, "\u{118c9}"), + (0x118AA, "\u{118ca}"), + (0x118AB, "\u{118cb}"), + (0x118AC, "\u{118cc}"), + (0x118AD, "\u{118cd}"), + (0x118AE, "\u{118ce}"), + (0x118AF, "\u{118cf}"), + (0x118B0, "\u{118d0}"), + (0x118B1, "\u{118d1}"), + (0x118B2, "\u{118d2}"), + (0x118B3, "\u{118d3}"), + (0x118B4, "\u{118d4}"), + (0x118B5, "\u{118d5}"), + (0x118B6, "\u{118d6}"), + (0x118B7, "\u{118d7}"), + (0x118B8, "\u{118d8}"), + (0x118B9, "\u{118d9}"), + (0x118BA, "\u{118da}"), + (0x118BB, "\u{118db}"), + (0x118BC, "\u{118dc}"), + (0x118BD, "\u{118dd}"), + (0x118BE, "\u{118de}"), + (0x118BF, "\u{118df}"), + (0x16E40, "\u{16e60}"), + (0x16E41, "\u{16e61}"), + (0x16E42, "\u{16e62}"), + (0x16E43, "\u{16e63}"), + (0x16E44, "\u{16e64}"), + (0x16E45, "\u{16e65}"), + (0x16E46, "\u{16e66}"), + (0x16E47, "\u{16e67}"), + (0x16E48, "\u{16e68}"), + (0x16E49, "\u{16e69}"), + (0x16E4A, "\u{16e6a}"), + (0x16E4B, "\u{16e6b}"), + (0x16E4C, "\u{16e6c}"), + (0x16E4D, "\u{16e6d}"), + (0x16E4E, "\u{16e6e}"), + (0x16E4F, "\u{16e6f}"), + (0x16E50, "\u{16e70}"), + (0x16E51, "\u{16e71}"), + (0x16E52, "\u{16e72}"), + (0x16E53, "\u{16e73}"), + (0x16E54, "\u{16e74}"), + (0x16E55, "\u{16e75}"), + (0x16E56, "\u{16e76}"), + (0x16E57, "\u{16e77}"), + (0x16E58, "\u{16e78}"), + (0x16E59, "\u{16e79}"), + (0x16E5A, "\u{16e7a}"), + (0x16E5B, "\u{16e7b}"), + (0x16E5C, "\u{16e7c}"), + (0x16E5D, "\u{16e7d}"), + (0x16E5E, "\u{16e7e}"), + (0x16E5F, "\u{16e7f}"), + (0x1E900, "\u{1e922}"), + (0x1E901, "\u{1e923}"), + (0x1E902, "\u{1e924}"), + (0x1E903, "\u{1e925}"), + (0x1E904, "\u{1e926}"), + (0x1E905, "\u{1e927}"), + (0x1E906, "\u{1e928}"), + (0x1E907, "\u{1e929}"), + (0x1E908, "\u{1e92a}"), + (0x1E909, "\u{1e92b}"), + (0x1E90A, "\u{1e92c}"), + (0x1E90B, "\u{1e92d}"), + (0x1E90C, "\u{1e92e}"), + (0x1E90D, "\u{1e92f}"), + (0x1E90E, "\u{1e930}"), + (0x1E90F, "\u{1e931}"), + (0x1E910, "\u{1e932}"), + (0x1E911, "\u{1e933}"), + (0x1E912, "\u{1e934}"), + (0x1E913, "\u{1e935}"), + (0x1E914, "\u{1e936}"), + (0x1E915, "\u{1e937}"), + (0x1E916, "\u{1e938}"), + (0x1E917, "\u{1e939}"), + (0x1E918, "\u{1e93a}"), + (0x1E919, "\u{1e93b}"), + (0x1E91A, "\u{1e93c}"), + (0x1E91B, "\u{1e93d}"), + (0x1E91C, "\u{1e93e}"), + (0x1E91D, "\u{1e93f}"), + (0x1E91E, "\u{1e940}"), + (0x1E91F, "\u{1e941}"), + (0x1E920, "\u{1e942}"), + (0x1E921, "\u{1e943}"), +] diff --git a/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_1_0.in b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_1_0.in new file mode 100644 index 0000000000..8e038fdd13 --- /dev/null +++ b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_15_1_0.in @@ -0,0 +1,1534 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.casefold()` changes in Unicode 15.1.0, in code point order. +&[ + (0x0041, "\u{61}"), + (0x0042, "\u{62}"), + (0x0043, "\u{63}"), + (0x0044, "\u{64}"), + (0x0045, "\u{65}"), + (0x0046, "\u{66}"), + (0x0047, "\u{67}"), + (0x0048, "\u{68}"), + (0x0049, "\u{69}"), + (0x004A, "\u{6a}"), + (0x004B, "\u{6b}"), + (0x004C, "\u{6c}"), + (0x004D, "\u{6d}"), + (0x004E, "\u{6e}"), + (0x004F, "\u{6f}"), + (0x0050, "\u{70}"), + (0x0051, "\u{71}"), + (0x0052, "\u{72}"), + (0x0053, "\u{73}"), + (0x0054, "\u{74}"), + (0x0055, "\u{75}"), + (0x0056, "\u{76}"), + (0x0057, "\u{77}"), + (0x0058, "\u{78}"), + (0x0059, "\u{79}"), + (0x005A, "\u{7a}"), + (0x00B5, "\u{3bc}"), + (0x00C0, "\u{e0}"), + (0x00C1, "\u{e1}"), + (0x00C2, "\u{e2}"), + (0x00C3, "\u{e3}"), + (0x00C4, "\u{e4}"), + (0x00C5, "\u{e5}"), + (0x00C6, "\u{e6}"), + (0x00C7, "\u{e7}"), + (0x00C8, "\u{e8}"), + (0x00C9, "\u{e9}"), + (0x00CA, "\u{ea}"), + (0x00CB, "\u{eb}"), + (0x00CC, "\u{ec}"), + (0x00CD, "\u{ed}"), + (0x00CE, "\u{ee}"), + (0x00CF, "\u{ef}"), + (0x00D0, "\u{f0}"), + (0x00D1, "\u{f1}"), + (0x00D2, "\u{f2}"), + (0x00D3, "\u{f3}"), + (0x00D4, "\u{f4}"), + (0x00D5, "\u{f5}"), + (0x00D6, "\u{f6}"), + (0x00D8, "\u{f8}"), + (0x00D9, "\u{f9}"), + (0x00DA, "\u{fa}"), + (0x00DB, "\u{fb}"), + (0x00DC, "\u{fc}"), + (0x00DD, "\u{fd}"), + (0x00DE, "\u{fe}"), + (0x00DF, "\u{73}\u{73}"), + (0x0100, "\u{101}"), + (0x0102, "\u{103}"), + (0x0104, "\u{105}"), + (0x0106, "\u{107}"), + (0x0108, "\u{109}"), + (0x010A, "\u{10b}"), + (0x010C, "\u{10d}"), + (0x010E, "\u{10f}"), + (0x0110, "\u{111}"), + (0x0112, "\u{113}"), + (0x0114, "\u{115}"), + (0x0116, "\u{117}"), + (0x0118, "\u{119}"), + (0x011A, "\u{11b}"), + (0x011C, "\u{11d}"), + (0x011E, "\u{11f}"), + (0x0120, "\u{121}"), + (0x0122, "\u{123}"), + (0x0124, "\u{125}"), + (0x0126, "\u{127}"), + (0x0128, "\u{129}"), + (0x012A, "\u{12b}"), + (0x012C, "\u{12d}"), + (0x012E, "\u{12f}"), + (0x0130, "\u{69}\u{307}"), + (0x0132, "\u{133}"), + (0x0134, "\u{135}"), + (0x0136, "\u{137}"), + (0x0139, "\u{13a}"), + (0x013B, "\u{13c}"), + (0x013D, "\u{13e}"), + (0x013F, "\u{140}"), + (0x0141, "\u{142}"), + (0x0143, "\u{144}"), + (0x0145, "\u{146}"), + (0x0147, "\u{148}"), + (0x0149, "\u{2bc}\u{6e}"), + (0x014A, "\u{14b}"), + (0x014C, "\u{14d}"), + (0x014E, "\u{14f}"), + (0x0150, "\u{151}"), + (0x0152, "\u{153}"), + (0x0154, "\u{155}"), + (0x0156, "\u{157}"), + (0x0158, "\u{159}"), + (0x015A, "\u{15b}"), + (0x015C, "\u{15d}"), + (0x015E, "\u{15f}"), + (0x0160, "\u{161}"), + (0x0162, "\u{163}"), + (0x0164, "\u{165}"), + (0x0166, "\u{167}"), + (0x0168, "\u{169}"), + (0x016A, "\u{16b}"), + (0x016C, "\u{16d}"), + (0x016E, "\u{16f}"), + (0x0170, "\u{171}"), + (0x0172, "\u{173}"), + (0x0174, "\u{175}"), + (0x0176, "\u{177}"), + (0x0178, "\u{ff}"), + (0x0179, "\u{17a}"), + (0x017B, "\u{17c}"), + (0x017D, "\u{17e}"), + (0x017F, "\u{73}"), + (0x0181, "\u{253}"), + (0x0182, "\u{183}"), + (0x0184, "\u{185}"), + (0x0186, "\u{254}"), + (0x0187, "\u{188}"), + (0x0189, "\u{256}"), + (0x018A, "\u{257}"), + (0x018B, "\u{18c}"), + (0x018E, "\u{1dd}"), + (0x018F, "\u{259}"), + (0x0190, "\u{25b}"), + (0x0191, "\u{192}"), + (0x0193, "\u{260}"), + (0x0194, "\u{263}"), + (0x0196, "\u{269}"), + (0x0197, "\u{268}"), + (0x0198, "\u{199}"), + (0x019C, "\u{26f}"), + (0x019D, "\u{272}"), + (0x019F, "\u{275}"), + (0x01A0, "\u{1a1}"), + (0x01A2, "\u{1a3}"), + (0x01A4, "\u{1a5}"), + (0x01A6, "\u{280}"), + (0x01A7, "\u{1a8}"), + (0x01A9, "\u{283}"), + (0x01AC, "\u{1ad}"), + (0x01AE, "\u{288}"), + (0x01AF, "\u{1b0}"), + (0x01B1, "\u{28a}"), + (0x01B2, "\u{28b}"), + (0x01B3, "\u{1b4}"), + (0x01B5, "\u{1b6}"), + (0x01B7, "\u{292}"), + (0x01B8, "\u{1b9}"), + (0x01BC, "\u{1bd}"), + (0x01C4, "\u{1c6}"), + (0x01C5, "\u{1c6}"), + (0x01C7, "\u{1c9}"), + (0x01C8, "\u{1c9}"), + (0x01CA, "\u{1cc}"), + (0x01CB, "\u{1cc}"), + (0x01CD, "\u{1ce}"), + (0x01CF, "\u{1d0}"), + (0x01D1, "\u{1d2}"), + (0x01D3, "\u{1d4}"), + (0x01D5, "\u{1d6}"), + (0x01D7, "\u{1d8}"), + (0x01D9, "\u{1da}"), + (0x01DB, "\u{1dc}"), + (0x01DE, "\u{1df}"), + (0x01E0, "\u{1e1}"), + (0x01E2, "\u{1e3}"), + (0x01E4, "\u{1e5}"), + (0x01E6, "\u{1e7}"), + (0x01E8, "\u{1e9}"), + (0x01EA, "\u{1eb}"), + (0x01EC, "\u{1ed}"), + (0x01EE, "\u{1ef}"), + (0x01F0, "\u{6a}\u{30c}"), + (0x01F1, "\u{1f3}"), + (0x01F2, "\u{1f3}"), + (0x01F4, "\u{1f5}"), + (0x01F6, "\u{195}"), + (0x01F7, "\u{1bf}"), + (0x01F8, "\u{1f9}"), + (0x01FA, "\u{1fb}"), + (0x01FC, "\u{1fd}"), + (0x01FE, "\u{1ff}"), + (0x0200, "\u{201}"), + (0x0202, "\u{203}"), + (0x0204, "\u{205}"), + (0x0206, "\u{207}"), + (0x0208, "\u{209}"), + (0x020A, "\u{20b}"), + (0x020C, "\u{20d}"), + (0x020E, "\u{20f}"), + (0x0210, "\u{211}"), + (0x0212, "\u{213}"), + (0x0214, "\u{215}"), + (0x0216, "\u{217}"), + (0x0218, "\u{219}"), + (0x021A, "\u{21b}"), + (0x021C, "\u{21d}"), + (0x021E, "\u{21f}"), + (0x0220, "\u{19e}"), + (0x0222, "\u{223}"), + (0x0224, "\u{225}"), + (0x0226, "\u{227}"), + (0x0228, "\u{229}"), + (0x022A, "\u{22b}"), + (0x022C, "\u{22d}"), + (0x022E, "\u{22f}"), + (0x0230, "\u{231}"), + (0x0232, "\u{233}"), + (0x023A, "\u{2c65}"), + (0x023B, "\u{23c}"), + (0x023D, "\u{19a}"), + (0x023E, "\u{2c66}"), + (0x0241, "\u{242}"), + (0x0243, "\u{180}"), + (0x0244, "\u{289}"), + (0x0245, "\u{28c}"), + (0x0246, "\u{247}"), + (0x0248, "\u{249}"), + (0x024A, "\u{24b}"), + (0x024C, "\u{24d}"), + (0x024E, "\u{24f}"), + (0x0345, "\u{3b9}"), + (0x0370, "\u{371}"), + (0x0372, "\u{373}"), + (0x0376, "\u{377}"), + (0x037F, "\u{3f3}"), + (0x0386, "\u{3ac}"), + (0x0388, "\u{3ad}"), + (0x0389, "\u{3ae}"), + (0x038A, "\u{3af}"), + (0x038C, "\u{3cc}"), + (0x038E, "\u{3cd}"), + (0x038F, "\u{3ce}"), + (0x0390, "\u{3b9}\u{308}\u{301}"), + (0x0391, "\u{3b1}"), + (0x0392, "\u{3b2}"), + (0x0393, "\u{3b3}"), + (0x0394, "\u{3b4}"), + (0x0395, "\u{3b5}"), + (0x0396, "\u{3b6}"), + (0x0397, "\u{3b7}"), + (0x0398, "\u{3b8}"), + (0x0399, "\u{3b9}"), + (0x039A, "\u{3ba}"), + (0x039B, "\u{3bb}"), + (0x039C, "\u{3bc}"), + (0x039D, "\u{3bd}"), + (0x039E, "\u{3be}"), + (0x039F, "\u{3bf}"), + (0x03A0, "\u{3c0}"), + (0x03A1, "\u{3c1}"), + (0x03A3, "\u{3c3}"), + (0x03A4, "\u{3c4}"), + (0x03A5, "\u{3c5}"), + (0x03A6, "\u{3c6}"), + (0x03A7, "\u{3c7}"), + (0x03A8, "\u{3c8}"), + (0x03A9, "\u{3c9}"), + (0x03AA, "\u{3ca}"), + (0x03AB, "\u{3cb}"), + (0x03B0, "\u{3c5}\u{308}\u{301}"), + (0x03C2, "\u{3c3}"), + (0x03CF, "\u{3d7}"), + (0x03D0, "\u{3b2}"), + (0x03D1, "\u{3b8}"), + (0x03D5, "\u{3c6}"), + (0x03D6, "\u{3c0}"), + (0x03D8, "\u{3d9}"), + (0x03DA, "\u{3db}"), + (0x03DC, "\u{3dd}"), + (0x03DE, "\u{3df}"), + (0x03E0, "\u{3e1}"), + (0x03E2, "\u{3e3}"), + (0x03E4, "\u{3e5}"), + (0x03E6, "\u{3e7}"), + (0x03E8, "\u{3e9}"), + (0x03EA, "\u{3eb}"), + (0x03EC, "\u{3ed}"), + (0x03EE, "\u{3ef}"), + (0x03F0, "\u{3ba}"), + (0x03F1, "\u{3c1}"), + (0x03F4, "\u{3b8}"), + (0x03F5, "\u{3b5}"), + (0x03F7, "\u{3f8}"), + (0x03F9, "\u{3f2}"), + (0x03FA, "\u{3fb}"), + (0x03FD, "\u{37b}"), + (0x03FE, "\u{37c}"), + (0x03FF, "\u{37d}"), + (0x0400, "\u{450}"), + (0x0401, "\u{451}"), + (0x0402, "\u{452}"), + (0x0403, "\u{453}"), + (0x0404, "\u{454}"), + (0x0405, "\u{455}"), + (0x0406, "\u{456}"), + (0x0407, "\u{457}"), + (0x0408, "\u{458}"), + (0x0409, "\u{459}"), + (0x040A, "\u{45a}"), + (0x040B, "\u{45b}"), + (0x040C, "\u{45c}"), + (0x040D, "\u{45d}"), + (0x040E, "\u{45e}"), + (0x040F, "\u{45f}"), + (0x0410, "\u{430}"), + (0x0411, "\u{431}"), + (0x0412, "\u{432}"), + (0x0413, "\u{433}"), + (0x0414, "\u{434}"), + (0x0415, "\u{435}"), + (0x0416, "\u{436}"), + (0x0417, "\u{437}"), + (0x0418, "\u{438}"), + (0x0419, "\u{439}"), + (0x041A, "\u{43a}"), + (0x041B, "\u{43b}"), + (0x041C, "\u{43c}"), + (0x041D, "\u{43d}"), + (0x041E, "\u{43e}"), + (0x041F, "\u{43f}"), + (0x0420, "\u{440}"), + (0x0421, "\u{441}"), + (0x0422, "\u{442}"), + (0x0423, "\u{443}"), + (0x0424, "\u{444}"), + (0x0425, "\u{445}"), + (0x0426, "\u{446}"), + (0x0427, "\u{447}"), + (0x0428, "\u{448}"), + (0x0429, "\u{449}"), + (0x042A, "\u{44a}"), + (0x042B, "\u{44b}"), + (0x042C, "\u{44c}"), + (0x042D, "\u{44d}"), + (0x042E, "\u{44e}"), + (0x042F, "\u{44f}"), + (0x0460, "\u{461}"), + (0x0462, "\u{463}"), + (0x0464, "\u{465}"), + (0x0466, "\u{467}"), + (0x0468, "\u{469}"), + (0x046A, "\u{46b}"), + (0x046C, "\u{46d}"), + (0x046E, "\u{46f}"), + (0x0470, "\u{471}"), + (0x0472, "\u{473}"), + (0x0474, "\u{475}"), + (0x0476, "\u{477}"), + (0x0478, "\u{479}"), + (0x047A, "\u{47b}"), + (0x047C, "\u{47d}"), + (0x047E, "\u{47f}"), + (0x0480, "\u{481}"), + (0x048A, "\u{48b}"), + (0x048C, "\u{48d}"), + (0x048E, "\u{48f}"), + (0x0490, "\u{491}"), + (0x0492, "\u{493}"), + (0x0494, "\u{495}"), + (0x0496, "\u{497}"), + (0x0498, "\u{499}"), + (0x049A, "\u{49b}"), + (0x049C, "\u{49d}"), + (0x049E, "\u{49f}"), + (0x04A0, "\u{4a1}"), + (0x04A2, "\u{4a3}"), + (0x04A4, "\u{4a5}"), + (0x04A6, "\u{4a7}"), + (0x04A8, "\u{4a9}"), + (0x04AA, "\u{4ab}"), + (0x04AC, "\u{4ad}"), + (0x04AE, "\u{4af}"), + (0x04B0, "\u{4b1}"), + (0x04B2, "\u{4b3}"), + (0x04B4, "\u{4b5}"), + (0x04B6, "\u{4b7}"), + (0x04B8, "\u{4b9}"), + (0x04BA, "\u{4bb}"), + (0x04BC, "\u{4bd}"), + (0x04BE, "\u{4bf}"), + (0x04C0, "\u{4cf}"), + (0x04C1, "\u{4c2}"), + (0x04C3, "\u{4c4}"), + (0x04C5, "\u{4c6}"), + (0x04C7, "\u{4c8}"), + (0x04C9, "\u{4ca}"), + (0x04CB, "\u{4cc}"), + (0x04CD, "\u{4ce}"), + (0x04D0, "\u{4d1}"), + (0x04D2, "\u{4d3}"), + (0x04D4, "\u{4d5}"), + (0x04D6, "\u{4d7}"), + (0x04D8, "\u{4d9}"), + (0x04DA, "\u{4db}"), + (0x04DC, "\u{4dd}"), + (0x04DE, "\u{4df}"), + (0x04E0, "\u{4e1}"), + (0x04E2, "\u{4e3}"), + (0x04E4, "\u{4e5}"), + (0x04E6, "\u{4e7}"), + (0x04E8, "\u{4e9}"), + (0x04EA, "\u{4eb}"), + (0x04EC, "\u{4ed}"), + (0x04EE, "\u{4ef}"), + (0x04F0, "\u{4f1}"), + (0x04F2, "\u{4f3}"), + (0x04F4, "\u{4f5}"), + (0x04F6, "\u{4f7}"), + (0x04F8, "\u{4f9}"), + (0x04FA, "\u{4fb}"), + (0x04FC, "\u{4fd}"), + (0x04FE, "\u{4ff}"), + (0x0500, "\u{501}"), + (0x0502, "\u{503}"), + (0x0504, "\u{505}"), + (0x0506, "\u{507}"), + (0x0508, "\u{509}"), + (0x050A, "\u{50b}"), + (0x050C, "\u{50d}"), + (0x050E, "\u{50f}"), + (0x0510, "\u{511}"), + (0x0512, "\u{513}"), + (0x0514, "\u{515}"), + (0x0516, "\u{517}"), + (0x0518, "\u{519}"), + (0x051A, "\u{51b}"), + (0x051C, "\u{51d}"), + (0x051E, "\u{51f}"), + (0x0520, "\u{521}"), + (0x0522, "\u{523}"), + (0x0524, "\u{525}"), + (0x0526, "\u{527}"), + (0x0528, "\u{529}"), + (0x052A, "\u{52b}"), + (0x052C, "\u{52d}"), + (0x052E, "\u{52f}"), + (0x0531, "\u{561}"), + (0x0532, "\u{562}"), + (0x0533, "\u{563}"), + (0x0534, "\u{564}"), + (0x0535, "\u{565}"), + (0x0536, "\u{566}"), + (0x0537, "\u{567}"), + (0x0538, "\u{568}"), + (0x0539, "\u{569}"), + (0x053A, "\u{56a}"), + (0x053B, "\u{56b}"), + (0x053C, "\u{56c}"), + (0x053D, "\u{56d}"), + (0x053E, "\u{56e}"), + (0x053F, "\u{56f}"), + (0x0540, "\u{570}"), + (0x0541, "\u{571}"), + (0x0542, "\u{572}"), + (0x0543, "\u{573}"), + (0x0544, "\u{574}"), + (0x0545, "\u{575}"), + (0x0546, "\u{576}"), + (0x0547, "\u{577}"), + (0x0548, "\u{578}"), + (0x0549, "\u{579}"), + (0x054A, "\u{57a}"), + (0x054B, "\u{57b}"), + (0x054C, "\u{57c}"), + (0x054D, "\u{57d}"), + (0x054E, "\u{57e}"), + (0x054F, "\u{57f}"), + (0x0550, "\u{580}"), + (0x0551, "\u{581}"), + (0x0552, "\u{582}"), + (0x0553, "\u{583}"), + (0x0554, "\u{584}"), + (0x0555, "\u{585}"), + (0x0556, "\u{586}"), + (0x0587, "\u{565}\u{582}"), + (0x10A0, "\u{2d00}"), + (0x10A1, "\u{2d01}"), + (0x10A2, "\u{2d02}"), + (0x10A3, "\u{2d03}"), + (0x10A4, "\u{2d04}"), + (0x10A5, "\u{2d05}"), + (0x10A6, "\u{2d06}"), + (0x10A7, "\u{2d07}"), + (0x10A8, "\u{2d08}"), + (0x10A9, "\u{2d09}"), + (0x10AA, "\u{2d0a}"), + (0x10AB, "\u{2d0b}"), + (0x10AC, "\u{2d0c}"), + (0x10AD, "\u{2d0d}"), + (0x10AE, "\u{2d0e}"), + (0x10AF, "\u{2d0f}"), + (0x10B0, "\u{2d10}"), + (0x10B1, "\u{2d11}"), + (0x10B2, "\u{2d12}"), + (0x10B3, "\u{2d13}"), + (0x10B4, "\u{2d14}"), + (0x10B5, "\u{2d15}"), + (0x10B6, "\u{2d16}"), + (0x10B7, "\u{2d17}"), + (0x10B8, "\u{2d18}"), + (0x10B9, "\u{2d19}"), + (0x10BA, "\u{2d1a}"), + (0x10BB, "\u{2d1b}"), + (0x10BC, "\u{2d1c}"), + (0x10BD, "\u{2d1d}"), + (0x10BE, "\u{2d1e}"), + (0x10BF, "\u{2d1f}"), + (0x10C0, "\u{2d20}"), + (0x10C1, "\u{2d21}"), + (0x10C2, "\u{2d22}"), + (0x10C3, "\u{2d23}"), + (0x10C4, "\u{2d24}"), + (0x10C5, "\u{2d25}"), + (0x10C7, "\u{2d27}"), + (0x10CD, "\u{2d2d}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{432}"), + (0x1C81, "\u{434}"), + (0x1C82, "\u{43e}"), + (0x1C83, "\u{441}"), + (0x1C84, "\u{442}"), + (0x1C85, "\u{442}"), + (0x1C86, "\u{44a}"), + (0x1C87, "\u{463}"), + (0x1C88, "\u{a64b}"), + (0x1C90, "\u{10d0}"), + (0x1C91, "\u{10d1}"), + (0x1C92, "\u{10d2}"), + (0x1C93, "\u{10d3}"), + (0x1C94, "\u{10d4}"), + (0x1C95, "\u{10d5}"), + (0x1C96, "\u{10d6}"), + (0x1C97, "\u{10d7}"), + (0x1C98, "\u{10d8}"), + (0x1C99, "\u{10d9}"), + (0x1C9A, "\u{10da}"), + (0x1C9B, "\u{10db}"), + (0x1C9C, "\u{10dc}"), + (0x1C9D, "\u{10dd}"), + (0x1C9E, "\u{10de}"), + (0x1C9F, "\u{10df}"), + (0x1CA0, "\u{10e0}"), + (0x1CA1, "\u{10e1}"), + (0x1CA2, "\u{10e2}"), + (0x1CA3, "\u{10e3}"), + (0x1CA4, "\u{10e4}"), + (0x1CA5, "\u{10e5}"), + (0x1CA6, "\u{10e6}"), + (0x1CA7, "\u{10e7}"), + (0x1CA8, "\u{10e8}"), + (0x1CA9, "\u{10e9}"), + (0x1CAA, "\u{10ea}"), + (0x1CAB, "\u{10eb}"), + (0x1CAC, "\u{10ec}"), + (0x1CAD, "\u{10ed}"), + (0x1CAE, "\u{10ee}"), + (0x1CAF, "\u{10ef}"), + (0x1CB0, "\u{10f0}"), + (0x1CB1, "\u{10f1}"), + (0x1CB2, "\u{10f2}"), + (0x1CB3, "\u{10f3}"), + (0x1CB4, "\u{10f4}"), + (0x1CB5, "\u{10f5}"), + (0x1CB6, "\u{10f6}"), + (0x1CB7, "\u{10f7}"), + (0x1CB8, "\u{10f8}"), + (0x1CB9, "\u{10f9}"), + (0x1CBA, "\u{10fa}"), + (0x1CBD, "\u{10fd}"), + (0x1CBE, "\u{10fe}"), + (0x1CBF, "\u{10ff}"), + (0x1E00, "\u{1e01}"), + (0x1E02, "\u{1e03}"), + (0x1E04, "\u{1e05}"), + (0x1E06, "\u{1e07}"), + (0x1E08, "\u{1e09}"), + (0x1E0A, "\u{1e0b}"), + (0x1E0C, "\u{1e0d}"), + (0x1E0E, "\u{1e0f}"), + (0x1E10, "\u{1e11}"), + (0x1E12, "\u{1e13}"), + (0x1E14, "\u{1e15}"), + (0x1E16, "\u{1e17}"), + (0x1E18, "\u{1e19}"), + (0x1E1A, "\u{1e1b}"), + (0x1E1C, "\u{1e1d}"), + (0x1E1E, "\u{1e1f}"), + (0x1E20, "\u{1e21}"), + (0x1E22, "\u{1e23}"), + (0x1E24, "\u{1e25}"), + (0x1E26, "\u{1e27}"), + (0x1E28, "\u{1e29}"), + (0x1E2A, "\u{1e2b}"), + (0x1E2C, "\u{1e2d}"), + (0x1E2E, "\u{1e2f}"), + (0x1E30, "\u{1e31}"), + (0x1E32, "\u{1e33}"), + (0x1E34, "\u{1e35}"), + (0x1E36, "\u{1e37}"), + (0x1E38, "\u{1e39}"), + (0x1E3A, "\u{1e3b}"), + (0x1E3C, "\u{1e3d}"), + (0x1E3E, "\u{1e3f}"), + (0x1E40, "\u{1e41}"), + (0x1E42, "\u{1e43}"), + (0x1E44, "\u{1e45}"), + (0x1E46, "\u{1e47}"), + (0x1E48, "\u{1e49}"), + (0x1E4A, "\u{1e4b}"), + (0x1E4C, "\u{1e4d}"), + (0x1E4E, "\u{1e4f}"), + (0x1E50, "\u{1e51}"), + (0x1E52, "\u{1e53}"), + (0x1E54, "\u{1e55}"), + (0x1E56, "\u{1e57}"), + (0x1E58, "\u{1e59}"), + (0x1E5A, "\u{1e5b}"), + (0x1E5C, "\u{1e5d}"), + (0x1E5E, "\u{1e5f}"), + (0x1E60, "\u{1e61}"), + (0x1E62, "\u{1e63}"), + (0x1E64, "\u{1e65}"), + (0x1E66, "\u{1e67}"), + (0x1E68, "\u{1e69}"), + (0x1E6A, "\u{1e6b}"), + (0x1E6C, "\u{1e6d}"), + (0x1E6E, "\u{1e6f}"), + (0x1E70, "\u{1e71}"), + (0x1E72, "\u{1e73}"), + (0x1E74, "\u{1e75}"), + (0x1E76, "\u{1e77}"), + (0x1E78, "\u{1e79}"), + (0x1E7A, "\u{1e7b}"), + (0x1E7C, "\u{1e7d}"), + (0x1E7E, "\u{1e7f}"), + (0x1E80, "\u{1e81}"), + (0x1E82, "\u{1e83}"), + (0x1E84, "\u{1e85}"), + (0x1E86, "\u{1e87}"), + (0x1E88, "\u{1e89}"), + (0x1E8A, "\u{1e8b}"), + (0x1E8C, "\u{1e8d}"), + (0x1E8E, "\u{1e8f}"), + (0x1E90, "\u{1e91}"), + (0x1E92, "\u{1e93}"), + (0x1E94, "\u{1e95}"), + (0x1E96, "\u{68}\u{331}"), + (0x1E97, "\u{74}\u{308}"), + (0x1E98, "\u{77}\u{30a}"), + (0x1E99, "\u{79}\u{30a}"), + (0x1E9A, "\u{61}\u{2be}"), + (0x1E9B, "\u{1e61}"), + (0x1E9E, "\u{73}\u{73}"), + (0x1EA0, "\u{1ea1}"), + (0x1EA2, "\u{1ea3}"), + (0x1EA4, "\u{1ea5}"), + (0x1EA6, "\u{1ea7}"), + (0x1EA8, "\u{1ea9}"), + (0x1EAA, "\u{1eab}"), + (0x1EAC, "\u{1ead}"), + (0x1EAE, "\u{1eaf}"), + (0x1EB0, "\u{1eb1}"), + (0x1EB2, "\u{1eb3}"), + (0x1EB4, "\u{1eb5}"), + (0x1EB6, "\u{1eb7}"), + (0x1EB8, "\u{1eb9}"), + (0x1EBA, "\u{1ebb}"), + (0x1EBC, "\u{1ebd}"), + (0x1EBE, "\u{1ebf}"), + (0x1EC0, "\u{1ec1}"), + (0x1EC2, "\u{1ec3}"), + (0x1EC4, "\u{1ec5}"), + (0x1EC6, "\u{1ec7}"), + (0x1EC8, "\u{1ec9}"), + (0x1ECA, "\u{1ecb}"), + (0x1ECC, "\u{1ecd}"), + (0x1ECE, "\u{1ecf}"), + (0x1ED0, "\u{1ed1}"), + (0x1ED2, "\u{1ed3}"), + (0x1ED4, "\u{1ed5}"), + (0x1ED6, "\u{1ed7}"), + (0x1ED8, "\u{1ed9}"), + (0x1EDA, "\u{1edb}"), + (0x1EDC, "\u{1edd}"), + (0x1EDE, "\u{1edf}"), + (0x1EE0, "\u{1ee1}"), + (0x1EE2, "\u{1ee3}"), + (0x1EE4, "\u{1ee5}"), + (0x1EE6, "\u{1ee7}"), + (0x1EE8, "\u{1ee9}"), + (0x1EEA, "\u{1eeb}"), + (0x1EEC, "\u{1eed}"), + (0x1EEE, "\u{1eef}"), + (0x1EF0, "\u{1ef1}"), + (0x1EF2, "\u{1ef3}"), + (0x1EF4, "\u{1ef5}"), + (0x1EF6, "\u{1ef7}"), + (0x1EF8, "\u{1ef9}"), + (0x1EFA, "\u{1efb}"), + (0x1EFC, "\u{1efd}"), + (0x1EFE, "\u{1eff}"), + (0x1F08, "\u{1f00}"), + (0x1F09, "\u{1f01}"), + (0x1F0A, "\u{1f02}"), + (0x1F0B, "\u{1f03}"), + (0x1F0C, "\u{1f04}"), + (0x1F0D, "\u{1f05}"), + (0x1F0E, "\u{1f06}"), + (0x1F0F, "\u{1f07}"), + (0x1F18, "\u{1f10}"), + (0x1F19, "\u{1f11}"), + (0x1F1A, "\u{1f12}"), + (0x1F1B, "\u{1f13}"), + (0x1F1C, "\u{1f14}"), + (0x1F1D, "\u{1f15}"), + (0x1F28, "\u{1f20}"), + (0x1F29, "\u{1f21}"), + (0x1F2A, "\u{1f22}"), + (0x1F2B, "\u{1f23}"), + (0x1F2C, "\u{1f24}"), + (0x1F2D, "\u{1f25}"), + (0x1F2E, "\u{1f26}"), + (0x1F2F, "\u{1f27}"), + (0x1F38, "\u{1f30}"), + (0x1F39, "\u{1f31}"), + (0x1F3A, "\u{1f32}"), + (0x1F3B, "\u{1f33}"), + (0x1F3C, "\u{1f34}"), + (0x1F3D, "\u{1f35}"), + (0x1F3E, "\u{1f36}"), + (0x1F3F, "\u{1f37}"), + (0x1F48, "\u{1f40}"), + (0x1F49, "\u{1f41}"), + (0x1F4A, "\u{1f42}"), + (0x1F4B, "\u{1f43}"), + (0x1F4C, "\u{1f44}"), + (0x1F4D, "\u{1f45}"), + (0x1F50, "\u{3c5}\u{313}"), + (0x1F52, "\u{3c5}\u{313}\u{300}"), + (0x1F54, "\u{3c5}\u{313}\u{301}"), + (0x1F56, "\u{3c5}\u{313}\u{342}"), + (0x1F59, "\u{1f51}"), + (0x1F5B, "\u{1f53}"), + (0x1F5D, "\u{1f55}"), + (0x1F5F, "\u{1f57}"), + (0x1F68, "\u{1f60}"), + (0x1F69, "\u{1f61}"), + (0x1F6A, "\u{1f62}"), + (0x1F6B, "\u{1f63}"), + (0x1F6C, "\u{1f64}"), + (0x1F6D, "\u{1f65}"), + (0x1F6E, "\u{1f66}"), + (0x1F6F, "\u{1f67}"), + (0x1F80, "\u{1f00}\u{3b9}"), + (0x1F81, "\u{1f01}\u{3b9}"), + (0x1F82, "\u{1f02}\u{3b9}"), + (0x1F83, "\u{1f03}\u{3b9}"), + (0x1F84, "\u{1f04}\u{3b9}"), + (0x1F85, "\u{1f05}\u{3b9}"), + (0x1F86, "\u{1f06}\u{3b9}"), + (0x1F87, "\u{1f07}\u{3b9}"), + (0x1F88, "\u{1f00}\u{3b9}"), + (0x1F89, "\u{1f01}\u{3b9}"), + (0x1F8A, "\u{1f02}\u{3b9}"), + (0x1F8B, "\u{1f03}\u{3b9}"), + (0x1F8C, "\u{1f04}\u{3b9}"), + (0x1F8D, "\u{1f05}\u{3b9}"), + (0x1F8E, "\u{1f06}\u{3b9}"), + (0x1F8F, "\u{1f07}\u{3b9}"), + (0x1F90, "\u{1f20}\u{3b9}"), + (0x1F91, "\u{1f21}\u{3b9}"), + (0x1F92, "\u{1f22}\u{3b9}"), + (0x1F93, "\u{1f23}\u{3b9}"), + (0x1F94, "\u{1f24}\u{3b9}"), + (0x1F95, "\u{1f25}\u{3b9}"), + (0x1F96, "\u{1f26}\u{3b9}"), + (0x1F97, "\u{1f27}\u{3b9}"), + (0x1F98, "\u{1f20}\u{3b9}"), + (0x1F99, "\u{1f21}\u{3b9}"), + (0x1F9A, "\u{1f22}\u{3b9}"), + (0x1F9B, "\u{1f23}\u{3b9}"), + (0x1F9C, "\u{1f24}\u{3b9}"), + (0x1F9D, "\u{1f25}\u{3b9}"), + (0x1F9E, "\u{1f26}\u{3b9}"), + (0x1F9F, "\u{1f27}\u{3b9}"), + (0x1FA0, "\u{1f60}\u{3b9}"), + (0x1FA1, "\u{1f61}\u{3b9}"), + (0x1FA2, "\u{1f62}\u{3b9}"), + (0x1FA3, "\u{1f63}\u{3b9}"), + (0x1FA4, "\u{1f64}\u{3b9}"), + (0x1FA5, "\u{1f65}\u{3b9}"), + (0x1FA6, "\u{1f66}\u{3b9}"), + (0x1FA7, "\u{1f67}\u{3b9}"), + (0x1FA8, "\u{1f60}\u{3b9}"), + (0x1FA9, "\u{1f61}\u{3b9}"), + (0x1FAA, "\u{1f62}\u{3b9}"), + (0x1FAB, "\u{1f63}\u{3b9}"), + (0x1FAC, "\u{1f64}\u{3b9}"), + (0x1FAD, "\u{1f65}\u{3b9}"), + (0x1FAE, "\u{1f66}\u{3b9}"), + (0x1FAF, "\u{1f67}\u{3b9}"), + (0x1FB2, "\u{1f70}\u{3b9}"), + (0x1FB3, "\u{3b1}\u{3b9}"), + (0x1FB4, "\u{3ac}\u{3b9}"), + (0x1FB6, "\u{3b1}\u{342}"), + (0x1FB7, "\u{3b1}\u{342}\u{3b9}"), + (0x1FB8, "\u{1fb0}"), + (0x1FB9, "\u{1fb1}"), + (0x1FBA, "\u{1f70}"), + (0x1FBB, "\u{1f71}"), + (0x1FBC, "\u{3b1}\u{3b9}"), + (0x1FBE, "\u{3b9}"), + (0x1FC2, "\u{1f74}\u{3b9}"), + (0x1FC3, "\u{3b7}\u{3b9}"), + (0x1FC4, "\u{3ae}\u{3b9}"), + (0x1FC6, "\u{3b7}\u{342}"), + (0x1FC7, "\u{3b7}\u{342}\u{3b9}"), + (0x1FC8, "\u{1f72}"), + (0x1FC9, "\u{1f73}"), + (0x1FCA, "\u{1f74}"), + (0x1FCB, "\u{1f75}"), + (0x1FCC, "\u{3b7}\u{3b9}"), + (0x1FD2, "\u{3b9}\u{308}\u{300}"), + (0x1FD3, "\u{3b9}\u{308}\u{301}"), + (0x1FD6, "\u{3b9}\u{342}"), + (0x1FD7, "\u{3b9}\u{308}\u{342}"), + (0x1FD8, "\u{1fd0}"), + (0x1FD9, "\u{1fd1}"), + (0x1FDA, "\u{1f76}"), + (0x1FDB, "\u{1f77}"), + (0x1FE2, "\u{3c5}\u{308}\u{300}"), + (0x1FE3, "\u{3c5}\u{308}\u{301}"), + (0x1FE4, "\u{3c1}\u{313}"), + (0x1FE6, "\u{3c5}\u{342}"), + (0x1FE7, "\u{3c5}\u{308}\u{342}"), + (0x1FE8, "\u{1fe0}"), + (0x1FE9, "\u{1fe1}"), + (0x1FEA, "\u{1f7a}"), + (0x1FEB, "\u{1f7b}"), + (0x1FEC, "\u{1fe5}"), + (0x1FF2, "\u{1f7c}\u{3b9}"), + (0x1FF3, "\u{3c9}\u{3b9}"), + (0x1FF4, "\u{3ce}\u{3b9}"), + (0x1FF6, "\u{3c9}\u{342}"), + (0x1FF7, "\u{3c9}\u{342}\u{3b9}"), + (0x1FF8, "\u{1f78}"), + (0x1FF9, "\u{1f79}"), + (0x1FFA, "\u{1f7c}"), + (0x1FFB, "\u{1f7d}"), + (0x1FFC, "\u{3c9}\u{3b9}"), + (0x2126, "\u{3c9}"), + (0x212A, "\u{6b}"), + (0x212B, "\u{e5}"), + (0x2132, "\u{214e}"), + (0x2160, "\u{2170}"), + (0x2161, "\u{2171}"), + (0x2162, "\u{2172}"), + (0x2163, "\u{2173}"), + (0x2164, "\u{2174}"), + (0x2165, "\u{2175}"), + (0x2166, "\u{2176}"), + (0x2167, "\u{2177}"), + (0x2168, "\u{2178}"), + (0x2169, "\u{2179}"), + (0x216A, "\u{217a}"), + (0x216B, "\u{217b}"), + (0x216C, "\u{217c}"), + (0x216D, "\u{217d}"), + (0x216E, "\u{217e}"), + (0x216F, "\u{217f}"), + (0x2183, "\u{2184}"), + (0x24B6, "\u{24d0}"), + (0x24B7, "\u{24d1}"), + (0x24B8, "\u{24d2}"), + (0x24B9, "\u{24d3}"), + (0x24BA, "\u{24d4}"), + (0x24BB, "\u{24d5}"), + (0x24BC, "\u{24d6}"), + (0x24BD, "\u{24d7}"), + (0x24BE, "\u{24d8}"), + (0x24BF, "\u{24d9}"), + (0x24C0, "\u{24da}"), + (0x24C1, "\u{24db}"), + (0x24C2, "\u{24dc}"), + (0x24C3, "\u{24dd}"), + (0x24C4, "\u{24de}"), + (0x24C5, "\u{24df}"), + (0x24C6, "\u{24e0}"), + (0x24C7, "\u{24e1}"), + (0x24C8, "\u{24e2}"), + (0x24C9, "\u{24e3}"), + (0x24CA, "\u{24e4}"), + (0x24CB, "\u{24e5}"), + (0x24CC, "\u{24e6}"), + (0x24CD, "\u{24e7}"), + (0x24CE, "\u{24e8}"), + (0x24CF, "\u{24e9}"), + (0x2C00, "\u{2c30}"), + (0x2C01, "\u{2c31}"), + (0x2C02, "\u{2c32}"), + (0x2C03, "\u{2c33}"), + (0x2C04, "\u{2c34}"), + (0x2C05, "\u{2c35}"), + (0x2C06, "\u{2c36}"), + (0x2C07, "\u{2c37}"), + (0x2C08, "\u{2c38}"), + (0x2C09, "\u{2c39}"), + (0x2C0A, "\u{2c3a}"), + (0x2C0B, "\u{2c3b}"), + (0x2C0C, "\u{2c3c}"), + (0x2C0D, "\u{2c3d}"), + (0x2C0E, "\u{2c3e}"), + (0x2C0F, "\u{2c3f}"), + (0x2C10, "\u{2c40}"), + (0x2C11, "\u{2c41}"), + (0x2C12, "\u{2c42}"), + (0x2C13, "\u{2c43}"), + (0x2C14, "\u{2c44}"), + (0x2C15, "\u{2c45}"), + (0x2C16, "\u{2c46}"), + (0x2C17, "\u{2c47}"), + (0x2C18, "\u{2c48}"), + (0x2C19, "\u{2c49}"), + (0x2C1A, "\u{2c4a}"), + (0x2C1B, "\u{2c4b}"), + (0x2C1C, "\u{2c4c}"), + (0x2C1D, "\u{2c4d}"), + (0x2C1E, "\u{2c4e}"), + (0x2C1F, "\u{2c4f}"), + (0x2C20, "\u{2c50}"), + (0x2C21, "\u{2c51}"), + (0x2C22, "\u{2c52}"), + (0x2C23, "\u{2c53}"), + (0x2C24, "\u{2c54}"), + (0x2C25, "\u{2c55}"), + (0x2C26, "\u{2c56}"), + (0x2C27, "\u{2c57}"), + (0x2C28, "\u{2c58}"), + (0x2C29, "\u{2c59}"), + (0x2C2A, "\u{2c5a}"), + (0x2C2B, "\u{2c5b}"), + (0x2C2C, "\u{2c5c}"), + (0x2C2D, "\u{2c5d}"), + (0x2C2E, "\u{2c5e}"), + (0x2C2F, "\u{2c5f}"), + (0x2C60, "\u{2c61}"), + (0x2C62, "\u{26b}"), + (0x2C63, "\u{1d7d}"), + (0x2C64, "\u{27d}"), + (0x2C67, "\u{2c68}"), + (0x2C69, "\u{2c6a}"), + (0x2C6B, "\u{2c6c}"), + (0x2C6D, "\u{251}"), + (0x2C6E, "\u{271}"), + (0x2C6F, "\u{250}"), + (0x2C70, "\u{252}"), + (0x2C72, "\u{2c73}"), + (0x2C75, "\u{2c76}"), + (0x2C7E, "\u{23f}"), + (0x2C7F, "\u{240}"), + (0x2C80, "\u{2c81}"), + (0x2C82, "\u{2c83}"), + (0x2C84, "\u{2c85}"), + (0x2C86, "\u{2c87}"), + (0x2C88, "\u{2c89}"), + (0x2C8A, "\u{2c8b}"), + (0x2C8C, "\u{2c8d}"), + (0x2C8E, "\u{2c8f}"), + (0x2C90, "\u{2c91}"), + (0x2C92, "\u{2c93}"), + (0x2C94, "\u{2c95}"), + (0x2C96, "\u{2c97}"), + (0x2C98, "\u{2c99}"), + (0x2C9A, "\u{2c9b}"), + (0x2C9C, "\u{2c9d}"), + (0x2C9E, "\u{2c9f}"), + (0x2CA0, "\u{2ca1}"), + (0x2CA2, "\u{2ca3}"), + (0x2CA4, "\u{2ca5}"), + (0x2CA6, "\u{2ca7}"), + (0x2CA8, "\u{2ca9}"), + (0x2CAA, "\u{2cab}"), + (0x2CAC, "\u{2cad}"), + (0x2CAE, "\u{2caf}"), + (0x2CB0, "\u{2cb1}"), + (0x2CB2, "\u{2cb3}"), + (0x2CB4, "\u{2cb5}"), + (0x2CB6, "\u{2cb7}"), + (0x2CB8, "\u{2cb9}"), + (0x2CBA, "\u{2cbb}"), + (0x2CBC, "\u{2cbd}"), + (0x2CBE, "\u{2cbf}"), + (0x2CC0, "\u{2cc1}"), + (0x2CC2, "\u{2cc3}"), + (0x2CC4, "\u{2cc5}"), + (0x2CC6, "\u{2cc7}"), + (0x2CC8, "\u{2cc9}"), + (0x2CCA, "\u{2ccb}"), + (0x2CCC, "\u{2ccd}"), + (0x2CCE, "\u{2ccf}"), + (0x2CD0, "\u{2cd1}"), + (0x2CD2, "\u{2cd3}"), + (0x2CD4, "\u{2cd5}"), + (0x2CD6, "\u{2cd7}"), + (0x2CD8, "\u{2cd9}"), + (0x2CDA, "\u{2cdb}"), + (0x2CDC, "\u{2cdd}"), + (0x2CDE, "\u{2cdf}"), + (0x2CE0, "\u{2ce1}"), + (0x2CE2, "\u{2ce3}"), + (0x2CEB, "\u{2cec}"), + (0x2CED, "\u{2cee}"), + (0x2CF2, "\u{2cf3}"), + (0xA640, "\u{a641}"), + (0xA642, "\u{a643}"), + (0xA644, "\u{a645}"), + (0xA646, "\u{a647}"), + (0xA648, "\u{a649}"), + (0xA64A, "\u{a64b}"), + (0xA64C, "\u{a64d}"), + (0xA64E, "\u{a64f}"), + (0xA650, "\u{a651}"), + (0xA652, "\u{a653}"), + (0xA654, "\u{a655}"), + (0xA656, "\u{a657}"), + (0xA658, "\u{a659}"), + (0xA65A, "\u{a65b}"), + (0xA65C, "\u{a65d}"), + (0xA65E, "\u{a65f}"), + (0xA660, "\u{a661}"), + (0xA662, "\u{a663}"), + (0xA664, "\u{a665}"), + (0xA666, "\u{a667}"), + (0xA668, "\u{a669}"), + (0xA66A, "\u{a66b}"), + (0xA66C, "\u{a66d}"), + (0xA680, "\u{a681}"), + (0xA682, "\u{a683}"), + (0xA684, "\u{a685}"), + (0xA686, "\u{a687}"), + (0xA688, "\u{a689}"), + (0xA68A, "\u{a68b}"), + (0xA68C, "\u{a68d}"), + (0xA68E, "\u{a68f}"), + (0xA690, "\u{a691}"), + (0xA692, "\u{a693}"), + (0xA694, "\u{a695}"), + (0xA696, "\u{a697}"), + (0xA698, "\u{a699}"), + (0xA69A, "\u{a69b}"), + (0xA722, "\u{a723}"), + (0xA724, "\u{a725}"), + (0xA726, "\u{a727}"), + (0xA728, "\u{a729}"), + (0xA72A, "\u{a72b}"), + (0xA72C, "\u{a72d}"), + (0xA72E, "\u{a72f}"), + (0xA732, "\u{a733}"), + (0xA734, "\u{a735}"), + (0xA736, "\u{a737}"), + (0xA738, "\u{a739}"), + (0xA73A, "\u{a73b}"), + (0xA73C, "\u{a73d}"), + (0xA73E, "\u{a73f}"), + (0xA740, "\u{a741}"), + (0xA742, "\u{a743}"), + (0xA744, "\u{a745}"), + (0xA746, "\u{a747}"), + (0xA748, "\u{a749}"), + (0xA74A, "\u{a74b}"), + (0xA74C, "\u{a74d}"), + (0xA74E, "\u{a74f}"), + (0xA750, "\u{a751}"), + (0xA752, "\u{a753}"), + (0xA754, "\u{a755}"), + (0xA756, "\u{a757}"), + (0xA758, "\u{a759}"), + (0xA75A, "\u{a75b}"), + (0xA75C, "\u{a75d}"), + (0xA75E, "\u{a75f}"), + (0xA760, "\u{a761}"), + (0xA762, "\u{a763}"), + (0xA764, "\u{a765}"), + (0xA766, "\u{a767}"), + (0xA768, "\u{a769}"), + (0xA76A, "\u{a76b}"), + (0xA76C, "\u{a76d}"), + (0xA76E, "\u{a76f}"), + (0xA779, "\u{a77a}"), + (0xA77B, "\u{a77c}"), + (0xA77D, "\u{1d79}"), + (0xA77E, "\u{a77f}"), + (0xA780, "\u{a781}"), + (0xA782, "\u{a783}"), + (0xA784, "\u{a785}"), + (0xA786, "\u{a787}"), + (0xA78B, "\u{a78c}"), + (0xA78D, "\u{265}"), + (0xA790, "\u{a791}"), + (0xA792, "\u{a793}"), + (0xA796, "\u{a797}"), + (0xA798, "\u{a799}"), + (0xA79A, "\u{a79b}"), + (0xA79C, "\u{a79d}"), + (0xA79E, "\u{a79f}"), + (0xA7A0, "\u{a7a1}"), + (0xA7A2, "\u{a7a3}"), + (0xA7A4, "\u{a7a5}"), + (0xA7A6, "\u{a7a7}"), + (0xA7A8, "\u{a7a9}"), + (0xA7AA, "\u{266}"), + (0xA7AB, "\u{25c}"), + (0xA7AC, "\u{261}"), + (0xA7AD, "\u{26c}"), + (0xA7AE, "\u{26a}"), + (0xA7B0, "\u{29e}"), + (0xA7B1, "\u{287}"), + (0xA7B2, "\u{29d}"), + (0xA7B3, "\u{ab53}"), + (0xA7B4, "\u{a7b5}"), + (0xA7B6, "\u{a7b7}"), + (0xA7B8, "\u{a7b9}"), + (0xA7BA, "\u{a7bb}"), + (0xA7BC, "\u{a7bd}"), + (0xA7BE, "\u{a7bf}"), + (0xA7C0, "\u{a7c1}"), + (0xA7C2, "\u{a7c3}"), + (0xA7C4, "\u{a794}"), + (0xA7C5, "\u{282}"), + (0xA7C6, "\u{1d8e}"), + (0xA7C7, "\u{a7c8}"), + (0xA7C9, "\u{a7ca}"), + (0xA7D0, "\u{a7d1}"), + (0xA7D6, "\u{a7d7}"), + (0xA7D8, "\u{a7d9}"), + (0xA7F5, "\u{a7f6}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{66}\u{66}"), + (0xFB01, "\u{66}\u{69}"), + (0xFB02, "\u{66}\u{6c}"), + (0xFB03, "\u{66}\u{66}\u{69}"), + (0xFB04, "\u{66}\u{66}\u{6c}"), + (0xFB05, "\u{73}\u{74}"), + (0xFB06, "\u{73}\u{74}"), + (0xFB13, "\u{574}\u{576}"), + (0xFB14, "\u{574}\u{565}"), + (0xFB15, "\u{574}\u{56b}"), + (0xFB16, "\u{57e}\u{576}"), + (0xFB17, "\u{574}\u{56d}"), + (0xFF21, "\u{ff41}"), + (0xFF22, "\u{ff42}"), + (0xFF23, "\u{ff43}"), + (0xFF24, "\u{ff44}"), + (0xFF25, "\u{ff45}"), + (0xFF26, "\u{ff46}"), + (0xFF27, "\u{ff47}"), + (0xFF28, "\u{ff48}"), + (0xFF29, "\u{ff49}"), + (0xFF2A, "\u{ff4a}"), + (0xFF2B, "\u{ff4b}"), + (0xFF2C, "\u{ff4c}"), + (0xFF2D, "\u{ff4d}"), + (0xFF2E, "\u{ff4e}"), + (0xFF2F, "\u{ff4f}"), + (0xFF30, "\u{ff50}"), + (0xFF31, "\u{ff51}"), + (0xFF32, "\u{ff52}"), + (0xFF33, "\u{ff53}"), + (0xFF34, "\u{ff54}"), + (0xFF35, "\u{ff55}"), + (0xFF36, "\u{ff56}"), + (0xFF37, "\u{ff57}"), + (0xFF38, "\u{ff58}"), + (0xFF39, "\u{ff59}"), + (0xFF3A, "\u{ff5a}"), + (0x10400, "\u{10428}"), + (0x10401, "\u{10429}"), + (0x10402, "\u{1042a}"), + (0x10403, "\u{1042b}"), + (0x10404, "\u{1042c}"), + (0x10405, "\u{1042d}"), + (0x10406, "\u{1042e}"), + (0x10407, "\u{1042f}"), + (0x10408, "\u{10430}"), + (0x10409, "\u{10431}"), + (0x1040A, "\u{10432}"), + (0x1040B, "\u{10433}"), + (0x1040C, "\u{10434}"), + (0x1040D, "\u{10435}"), + (0x1040E, "\u{10436}"), + (0x1040F, "\u{10437}"), + (0x10410, "\u{10438}"), + (0x10411, "\u{10439}"), + (0x10412, "\u{1043a}"), + (0x10413, "\u{1043b}"), + (0x10414, "\u{1043c}"), + (0x10415, "\u{1043d}"), + (0x10416, "\u{1043e}"), + (0x10417, "\u{1043f}"), + (0x10418, "\u{10440}"), + (0x10419, "\u{10441}"), + (0x1041A, "\u{10442}"), + (0x1041B, "\u{10443}"), + (0x1041C, "\u{10444}"), + (0x1041D, "\u{10445}"), + (0x1041E, "\u{10446}"), + (0x1041F, "\u{10447}"), + (0x10420, "\u{10448}"), + (0x10421, "\u{10449}"), + (0x10422, "\u{1044a}"), + (0x10423, "\u{1044b}"), + (0x10424, "\u{1044c}"), + (0x10425, "\u{1044d}"), + (0x10426, "\u{1044e}"), + (0x10427, "\u{1044f}"), + (0x104B0, "\u{104d8}"), + (0x104B1, "\u{104d9}"), + (0x104B2, "\u{104da}"), + (0x104B3, "\u{104db}"), + (0x104B4, "\u{104dc}"), + (0x104B5, "\u{104dd}"), + (0x104B6, "\u{104de}"), + (0x104B7, "\u{104df}"), + (0x104B8, "\u{104e0}"), + (0x104B9, "\u{104e1}"), + (0x104BA, "\u{104e2}"), + (0x104BB, "\u{104e3}"), + (0x104BC, "\u{104e4}"), + (0x104BD, "\u{104e5}"), + (0x104BE, "\u{104e6}"), + (0x104BF, "\u{104e7}"), + (0x104C0, "\u{104e8}"), + (0x104C1, "\u{104e9}"), + (0x104C2, "\u{104ea}"), + (0x104C3, "\u{104eb}"), + (0x104C4, "\u{104ec}"), + (0x104C5, "\u{104ed}"), + (0x104C6, "\u{104ee}"), + (0x104C7, "\u{104ef}"), + (0x104C8, "\u{104f0}"), + (0x104C9, "\u{104f1}"), + (0x104CA, "\u{104f2}"), + (0x104CB, "\u{104f3}"), + (0x104CC, "\u{104f4}"), + (0x104CD, "\u{104f5}"), + (0x104CE, "\u{104f6}"), + (0x104CF, "\u{104f7}"), + (0x104D0, "\u{104f8}"), + (0x104D1, "\u{104f9}"), + (0x104D2, "\u{104fa}"), + (0x104D3, "\u{104fb}"), + (0x10570, "\u{10597}"), + (0x10571, "\u{10598}"), + (0x10572, "\u{10599}"), + (0x10573, "\u{1059a}"), + (0x10574, "\u{1059b}"), + (0x10575, "\u{1059c}"), + (0x10576, "\u{1059d}"), + (0x10577, "\u{1059e}"), + (0x10578, "\u{1059f}"), + (0x10579, "\u{105a0}"), + (0x1057A, "\u{105a1}"), + (0x1057C, "\u{105a3}"), + (0x1057D, "\u{105a4}"), + (0x1057E, "\u{105a5}"), + (0x1057F, "\u{105a6}"), + (0x10580, "\u{105a7}"), + (0x10581, "\u{105a8}"), + (0x10582, "\u{105a9}"), + (0x10583, "\u{105aa}"), + (0x10584, "\u{105ab}"), + (0x10585, "\u{105ac}"), + (0x10586, "\u{105ad}"), + (0x10587, "\u{105ae}"), + (0x10588, "\u{105af}"), + (0x10589, "\u{105b0}"), + (0x1058A, "\u{105b1}"), + (0x1058C, "\u{105b3}"), + (0x1058D, "\u{105b4}"), + (0x1058E, "\u{105b5}"), + (0x1058F, "\u{105b6}"), + (0x10590, "\u{105b7}"), + (0x10591, "\u{105b8}"), + (0x10592, "\u{105b9}"), + (0x10594, "\u{105bb}"), + (0x10595, "\u{105bc}"), + (0x10C80, "\u{10cc0}"), + (0x10C81, "\u{10cc1}"), + (0x10C82, "\u{10cc2}"), + (0x10C83, "\u{10cc3}"), + (0x10C84, "\u{10cc4}"), + (0x10C85, "\u{10cc5}"), + (0x10C86, "\u{10cc6}"), + (0x10C87, "\u{10cc7}"), + (0x10C88, "\u{10cc8}"), + (0x10C89, "\u{10cc9}"), + (0x10C8A, "\u{10cca}"), + (0x10C8B, "\u{10ccb}"), + (0x10C8C, "\u{10ccc}"), + (0x10C8D, "\u{10ccd}"), + (0x10C8E, "\u{10cce}"), + (0x10C8F, "\u{10ccf}"), + (0x10C90, "\u{10cd0}"), + (0x10C91, "\u{10cd1}"), + (0x10C92, "\u{10cd2}"), + (0x10C93, "\u{10cd3}"), + (0x10C94, "\u{10cd4}"), + (0x10C95, "\u{10cd5}"), + (0x10C96, "\u{10cd6}"), + (0x10C97, "\u{10cd7}"), + (0x10C98, "\u{10cd8}"), + (0x10C99, "\u{10cd9}"), + (0x10C9A, "\u{10cda}"), + (0x10C9B, "\u{10cdb}"), + (0x10C9C, "\u{10cdc}"), + (0x10C9D, "\u{10cdd}"), + (0x10C9E, "\u{10cde}"), + (0x10C9F, "\u{10cdf}"), + (0x10CA0, "\u{10ce0}"), + (0x10CA1, "\u{10ce1}"), + (0x10CA2, "\u{10ce2}"), + (0x10CA3, "\u{10ce3}"), + (0x10CA4, "\u{10ce4}"), + (0x10CA5, "\u{10ce5}"), + (0x10CA6, "\u{10ce6}"), + (0x10CA7, "\u{10ce7}"), + (0x10CA8, "\u{10ce8}"), + (0x10CA9, "\u{10ce9}"), + (0x10CAA, "\u{10cea}"), + (0x10CAB, "\u{10ceb}"), + (0x10CAC, "\u{10cec}"), + (0x10CAD, "\u{10ced}"), + (0x10CAE, "\u{10cee}"), + (0x10CAF, "\u{10cef}"), + (0x10CB0, "\u{10cf0}"), + (0x10CB1, "\u{10cf1}"), + (0x10CB2, "\u{10cf2}"), + (0x118A0, "\u{118c0}"), + (0x118A1, "\u{118c1}"), + (0x118A2, "\u{118c2}"), + (0x118A3, "\u{118c3}"), + (0x118A4, "\u{118c4}"), + (0x118A5, "\u{118c5}"), + (0x118A6, "\u{118c6}"), + (0x118A7, "\u{118c7}"), + (0x118A8, "\u{118c8}"), + (0x118A9, "\u{118c9}"), + (0x118AA, "\u{118ca}"), + (0x118AB, "\u{118cb}"), + (0x118AC, "\u{118cc}"), + (0x118AD, "\u{118cd}"), + (0x118AE, "\u{118ce}"), + (0x118AF, "\u{118cf}"), + (0x118B0, "\u{118d0}"), + (0x118B1, "\u{118d1}"), + (0x118B2, "\u{118d2}"), + (0x118B3, "\u{118d3}"), + (0x118B4, "\u{118d4}"), + (0x118B5, "\u{118d5}"), + (0x118B6, "\u{118d6}"), + (0x118B7, "\u{118d7}"), + (0x118B8, "\u{118d8}"), + (0x118B9, "\u{118d9}"), + (0x118BA, "\u{118da}"), + (0x118BB, "\u{118db}"), + (0x118BC, "\u{118dc}"), + (0x118BD, "\u{118dd}"), + (0x118BE, "\u{118de}"), + (0x118BF, "\u{118df}"), + (0x16E40, "\u{16e60}"), + (0x16E41, "\u{16e61}"), + (0x16E42, "\u{16e62}"), + (0x16E43, "\u{16e63}"), + (0x16E44, "\u{16e64}"), + (0x16E45, "\u{16e65}"), + (0x16E46, "\u{16e66}"), + (0x16E47, "\u{16e67}"), + (0x16E48, "\u{16e68}"), + (0x16E49, "\u{16e69}"), + (0x16E4A, "\u{16e6a}"), + (0x16E4B, "\u{16e6b}"), + (0x16E4C, "\u{16e6c}"), + (0x16E4D, "\u{16e6d}"), + (0x16E4E, "\u{16e6e}"), + (0x16E4F, "\u{16e6f}"), + (0x16E50, "\u{16e70}"), + (0x16E51, "\u{16e71}"), + (0x16E52, "\u{16e72}"), + (0x16E53, "\u{16e73}"), + (0x16E54, "\u{16e74}"), + (0x16E55, "\u{16e75}"), + (0x16E56, "\u{16e76}"), + (0x16E57, "\u{16e77}"), + (0x16E58, "\u{16e78}"), + (0x16E59, "\u{16e79}"), + (0x16E5A, "\u{16e7a}"), + (0x16E5B, "\u{16e7b}"), + (0x16E5C, "\u{16e7c}"), + (0x16E5D, "\u{16e7d}"), + (0x16E5E, "\u{16e7e}"), + (0x16E5F, "\u{16e7f}"), + (0x1E900, "\u{1e922}"), + (0x1E901, "\u{1e923}"), + (0x1E902, "\u{1e924}"), + (0x1E903, "\u{1e925}"), + (0x1E904, "\u{1e926}"), + (0x1E905, "\u{1e927}"), + (0x1E906, "\u{1e928}"), + (0x1E907, "\u{1e929}"), + (0x1E908, "\u{1e92a}"), + (0x1E909, "\u{1e92b}"), + (0x1E90A, "\u{1e92c}"), + (0x1E90B, "\u{1e92d}"), + (0x1E90C, "\u{1e92e}"), + (0x1E90D, "\u{1e92f}"), + (0x1E90E, "\u{1e930}"), + (0x1E90F, "\u{1e931}"), + (0x1E910, "\u{1e932}"), + (0x1E911, "\u{1e933}"), + (0x1E912, "\u{1e934}"), + (0x1E913, "\u{1e935}"), + (0x1E914, "\u{1e936}"), + (0x1E915, "\u{1e937}"), + (0x1E916, "\u{1e938}"), + (0x1E917, "\u{1e939}"), + (0x1E918, "\u{1e93a}"), + (0x1E919, "\u{1e93b}"), + (0x1E91A, "\u{1e93c}"), + (0x1E91B, "\u{1e93d}"), + (0x1E91C, "\u{1e93e}"), + (0x1E91D, "\u{1e93f}"), + (0x1E91E, "\u{1e940}"), + (0x1E91F, "\u{1e941}"), + (0x1E920, "\u{1e942}"), + (0x1E921, "\u{1e943}"), +] diff --git a/crates/sqlbuild-core/src/text/casefold_mappings/unicode_16_0_0.in b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_16_0_0.in new file mode 100644 index 0000000000..281e3028bd --- /dev/null +++ b/crates/sqlbuild-core/src/text/casefold_mappings/unicode_16_0_0.in @@ -0,0 +1,1561 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.casefold()` changes in Unicode 16.0.0, in code point order. +&[ + (0x0041, "\u{61}"), + (0x0042, "\u{62}"), + (0x0043, "\u{63}"), + (0x0044, "\u{64}"), + (0x0045, "\u{65}"), + (0x0046, "\u{66}"), + (0x0047, "\u{67}"), + (0x0048, "\u{68}"), + (0x0049, "\u{69}"), + (0x004A, "\u{6a}"), + (0x004B, "\u{6b}"), + (0x004C, "\u{6c}"), + (0x004D, "\u{6d}"), + (0x004E, "\u{6e}"), + (0x004F, "\u{6f}"), + (0x0050, "\u{70}"), + (0x0051, "\u{71}"), + (0x0052, "\u{72}"), + (0x0053, "\u{73}"), + (0x0054, "\u{74}"), + (0x0055, "\u{75}"), + (0x0056, "\u{76}"), + (0x0057, "\u{77}"), + (0x0058, "\u{78}"), + (0x0059, "\u{79}"), + (0x005A, "\u{7a}"), + (0x00B5, "\u{3bc}"), + (0x00C0, "\u{e0}"), + (0x00C1, "\u{e1}"), + (0x00C2, "\u{e2}"), + (0x00C3, "\u{e3}"), + (0x00C4, "\u{e4}"), + (0x00C5, "\u{e5}"), + (0x00C6, "\u{e6}"), + (0x00C7, "\u{e7}"), + (0x00C8, "\u{e8}"), + (0x00C9, "\u{e9}"), + (0x00CA, "\u{ea}"), + (0x00CB, "\u{eb}"), + (0x00CC, "\u{ec}"), + (0x00CD, "\u{ed}"), + (0x00CE, "\u{ee}"), + (0x00CF, "\u{ef}"), + (0x00D0, "\u{f0}"), + (0x00D1, "\u{f1}"), + (0x00D2, "\u{f2}"), + (0x00D3, "\u{f3}"), + (0x00D4, "\u{f4}"), + (0x00D5, "\u{f5}"), + (0x00D6, "\u{f6}"), + (0x00D8, "\u{f8}"), + (0x00D9, "\u{f9}"), + (0x00DA, "\u{fa}"), + (0x00DB, "\u{fb}"), + (0x00DC, "\u{fc}"), + (0x00DD, "\u{fd}"), + (0x00DE, "\u{fe}"), + (0x00DF, "\u{73}\u{73}"), + (0x0100, "\u{101}"), + (0x0102, "\u{103}"), + (0x0104, "\u{105}"), + (0x0106, "\u{107}"), + (0x0108, "\u{109}"), + (0x010A, "\u{10b}"), + (0x010C, "\u{10d}"), + (0x010E, "\u{10f}"), + (0x0110, "\u{111}"), + (0x0112, "\u{113}"), + (0x0114, "\u{115}"), + (0x0116, "\u{117}"), + (0x0118, "\u{119}"), + (0x011A, "\u{11b}"), + (0x011C, "\u{11d}"), + (0x011E, "\u{11f}"), + (0x0120, "\u{121}"), + (0x0122, "\u{123}"), + (0x0124, "\u{125}"), + (0x0126, "\u{127}"), + (0x0128, "\u{129}"), + (0x012A, "\u{12b}"), + (0x012C, "\u{12d}"), + (0x012E, "\u{12f}"), + (0x0130, "\u{69}\u{307}"), + (0x0132, "\u{133}"), + (0x0134, "\u{135}"), + (0x0136, "\u{137}"), + (0x0139, "\u{13a}"), + (0x013B, "\u{13c}"), + (0x013D, "\u{13e}"), + (0x013F, "\u{140}"), + (0x0141, "\u{142}"), + (0x0143, "\u{144}"), + (0x0145, "\u{146}"), + (0x0147, "\u{148}"), + (0x0149, "\u{2bc}\u{6e}"), + (0x014A, "\u{14b}"), + (0x014C, "\u{14d}"), + (0x014E, "\u{14f}"), + (0x0150, "\u{151}"), + (0x0152, "\u{153}"), + (0x0154, "\u{155}"), + (0x0156, "\u{157}"), + (0x0158, "\u{159}"), + (0x015A, "\u{15b}"), + (0x015C, "\u{15d}"), + (0x015E, "\u{15f}"), + (0x0160, "\u{161}"), + (0x0162, "\u{163}"), + (0x0164, "\u{165}"), + (0x0166, "\u{167}"), + (0x0168, "\u{169}"), + (0x016A, "\u{16b}"), + (0x016C, "\u{16d}"), + (0x016E, "\u{16f}"), + (0x0170, "\u{171}"), + (0x0172, "\u{173}"), + (0x0174, "\u{175}"), + (0x0176, "\u{177}"), + (0x0178, "\u{ff}"), + (0x0179, "\u{17a}"), + (0x017B, "\u{17c}"), + (0x017D, "\u{17e}"), + (0x017F, "\u{73}"), + (0x0181, "\u{253}"), + (0x0182, "\u{183}"), + (0x0184, "\u{185}"), + (0x0186, "\u{254}"), + (0x0187, "\u{188}"), + (0x0189, "\u{256}"), + (0x018A, "\u{257}"), + (0x018B, "\u{18c}"), + (0x018E, "\u{1dd}"), + (0x018F, "\u{259}"), + (0x0190, "\u{25b}"), + (0x0191, "\u{192}"), + (0x0193, "\u{260}"), + (0x0194, "\u{263}"), + (0x0196, "\u{269}"), + (0x0197, "\u{268}"), + (0x0198, "\u{199}"), + (0x019C, "\u{26f}"), + (0x019D, "\u{272}"), + (0x019F, "\u{275}"), + (0x01A0, "\u{1a1}"), + (0x01A2, "\u{1a3}"), + (0x01A4, "\u{1a5}"), + (0x01A6, "\u{280}"), + (0x01A7, "\u{1a8}"), + (0x01A9, "\u{283}"), + (0x01AC, "\u{1ad}"), + (0x01AE, "\u{288}"), + (0x01AF, "\u{1b0}"), + (0x01B1, "\u{28a}"), + (0x01B2, "\u{28b}"), + (0x01B3, "\u{1b4}"), + (0x01B5, "\u{1b6}"), + (0x01B7, "\u{292}"), + (0x01B8, "\u{1b9}"), + (0x01BC, "\u{1bd}"), + (0x01C4, "\u{1c6}"), + (0x01C5, "\u{1c6}"), + (0x01C7, "\u{1c9}"), + (0x01C8, "\u{1c9}"), + (0x01CA, "\u{1cc}"), + (0x01CB, "\u{1cc}"), + (0x01CD, "\u{1ce}"), + (0x01CF, "\u{1d0}"), + (0x01D1, "\u{1d2}"), + (0x01D3, "\u{1d4}"), + (0x01D5, "\u{1d6}"), + (0x01D7, "\u{1d8}"), + (0x01D9, "\u{1da}"), + (0x01DB, "\u{1dc}"), + (0x01DE, "\u{1df}"), + (0x01E0, "\u{1e1}"), + (0x01E2, "\u{1e3}"), + (0x01E4, "\u{1e5}"), + (0x01E6, "\u{1e7}"), + (0x01E8, "\u{1e9}"), + (0x01EA, "\u{1eb}"), + (0x01EC, "\u{1ed}"), + (0x01EE, "\u{1ef}"), + (0x01F0, "\u{6a}\u{30c}"), + (0x01F1, "\u{1f3}"), + (0x01F2, "\u{1f3}"), + (0x01F4, "\u{1f5}"), + (0x01F6, "\u{195}"), + (0x01F7, "\u{1bf}"), + (0x01F8, "\u{1f9}"), + (0x01FA, "\u{1fb}"), + (0x01FC, "\u{1fd}"), + (0x01FE, "\u{1ff}"), + (0x0200, "\u{201}"), + (0x0202, "\u{203}"), + (0x0204, "\u{205}"), + (0x0206, "\u{207}"), + (0x0208, "\u{209}"), + (0x020A, "\u{20b}"), + (0x020C, "\u{20d}"), + (0x020E, "\u{20f}"), + (0x0210, "\u{211}"), + (0x0212, "\u{213}"), + (0x0214, "\u{215}"), + (0x0216, "\u{217}"), + (0x0218, "\u{219}"), + (0x021A, "\u{21b}"), + (0x021C, "\u{21d}"), + (0x021E, "\u{21f}"), + (0x0220, "\u{19e}"), + (0x0222, "\u{223}"), + (0x0224, "\u{225}"), + (0x0226, "\u{227}"), + (0x0228, "\u{229}"), + (0x022A, "\u{22b}"), + (0x022C, "\u{22d}"), + (0x022E, "\u{22f}"), + (0x0230, "\u{231}"), + (0x0232, "\u{233}"), + (0x023A, "\u{2c65}"), + (0x023B, "\u{23c}"), + (0x023D, "\u{19a}"), + (0x023E, "\u{2c66}"), + (0x0241, "\u{242}"), + (0x0243, "\u{180}"), + (0x0244, "\u{289}"), + (0x0245, "\u{28c}"), + (0x0246, "\u{247}"), + (0x0248, "\u{249}"), + (0x024A, "\u{24b}"), + (0x024C, "\u{24d}"), + (0x024E, "\u{24f}"), + (0x0345, "\u{3b9}"), + (0x0370, "\u{371}"), + (0x0372, "\u{373}"), + (0x0376, "\u{377}"), + (0x037F, "\u{3f3}"), + (0x0386, "\u{3ac}"), + (0x0388, "\u{3ad}"), + (0x0389, "\u{3ae}"), + (0x038A, "\u{3af}"), + (0x038C, "\u{3cc}"), + (0x038E, "\u{3cd}"), + (0x038F, "\u{3ce}"), + (0x0390, "\u{3b9}\u{308}\u{301}"), + (0x0391, "\u{3b1}"), + (0x0392, "\u{3b2}"), + (0x0393, "\u{3b3}"), + (0x0394, "\u{3b4}"), + (0x0395, "\u{3b5}"), + (0x0396, "\u{3b6}"), + (0x0397, "\u{3b7}"), + (0x0398, "\u{3b8}"), + (0x0399, "\u{3b9}"), + (0x039A, "\u{3ba}"), + (0x039B, "\u{3bb}"), + (0x039C, "\u{3bc}"), + (0x039D, "\u{3bd}"), + (0x039E, "\u{3be}"), + (0x039F, "\u{3bf}"), + (0x03A0, "\u{3c0}"), + (0x03A1, "\u{3c1}"), + (0x03A3, "\u{3c3}"), + (0x03A4, "\u{3c4}"), + (0x03A5, "\u{3c5}"), + (0x03A6, "\u{3c6}"), + (0x03A7, "\u{3c7}"), + (0x03A8, "\u{3c8}"), + (0x03A9, "\u{3c9}"), + (0x03AA, "\u{3ca}"), + (0x03AB, "\u{3cb}"), + (0x03B0, "\u{3c5}\u{308}\u{301}"), + (0x03C2, "\u{3c3}"), + (0x03CF, "\u{3d7}"), + (0x03D0, "\u{3b2}"), + (0x03D1, "\u{3b8}"), + (0x03D5, "\u{3c6}"), + (0x03D6, "\u{3c0}"), + (0x03D8, "\u{3d9}"), + (0x03DA, "\u{3db}"), + (0x03DC, "\u{3dd}"), + (0x03DE, "\u{3df}"), + (0x03E0, "\u{3e1}"), + (0x03E2, "\u{3e3}"), + (0x03E4, "\u{3e5}"), + (0x03E6, "\u{3e7}"), + (0x03E8, "\u{3e9}"), + (0x03EA, "\u{3eb}"), + (0x03EC, "\u{3ed}"), + (0x03EE, "\u{3ef}"), + (0x03F0, "\u{3ba}"), + (0x03F1, "\u{3c1}"), + (0x03F4, "\u{3b8}"), + (0x03F5, "\u{3b5}"), + (0x03F7, "\u{3f8}"), + (0x03F9, "\u{3f2}"), + (0x03FA, "\u{3fb}"), + (0x03FD, "\u{37b}"), + (0x03FE, "\u{37c}"), + (0x03FF, "\u{37d}"), + (0x0400, "\u{450}"), + (0x0401, "\u{451}"), + (0x0402, "\u{452}"), + (0x0403, "\u{453}"), + (0x0404, "\u{454}"), + (0x0405, "\u{455}"), + (0x0406, "\u{456}"), + (0x0407, "\u{457}"), + (0x0408, "\u{458}"), + (0x0409, "\u{459}"), + (0x040A, "\u{45a}"), + (0x040B, "\u{45b}"), + (0x040C, "\u{45c}"), + (0x040D, "\u{45d}"), + (0x040E, "\u{45e}"), + (0x040F, "\u{45f}"), + (0x0410, "\u{430}"), + (0x0411, "\u{431}"), + (0x0412, "\u{432}"), + (0x0413, "\u{433}"), + (0x0414, "\u{434}"), + (0x0415, "\u{435}"), + (0x0416, "\u{436}"), + (0x0417, "\u{437}"), + (0x0418, "\u{438}"), + (0x0419, "\u{439}"), + (0x041A, "\u{43a}"), + (0x041B, "\u{43b}"), + (0x041C, "\u{43c}"), + (0x041D, "\u{43d}"), + (0x041E, "\u{43e}"), + (0x041F, "\u{43f}"), + (0x0420, "\u{440}"), + (0x0421, "\u{441}"), + (0x0422, "\u{442}"), + (0x0423, "\u{443}"), + (0x0424, "\u{444}"), + (0x0425, "\u{445}"), + (0x0426, "\u{446}"), + (0x0427, "\u{447}"), + (0x0428, "\u{448}"), + (0x0429, "\u{449}"), + (0x042A, "\u{44a}"), + (0x042B, "\u{44b}"), + (0x042C, "\u{44c}"), + (0x042D, "\u{44d}"), + (0x042E, "\u{44e}"), + (0x042F, "\u{44f}"), + (0x0460, "\u{461}"), + (0x0462, "\u{463}"), + (0x0464, "\u{465}"), + (0x0466, "\u{467}"), + (0x0468, "\u{469}"), + (0x046A, "\u{46b}"), + (0x046C, "\u{46d}"), + (0x046E, "\u{46f}"), + (0x0470, "\u{471}"), + (0x0472, "\u{473}"), + (0x0474, "\u{475}"), + (0x0476, "\u{477}"), + (0x0478, "\u{479}"), + (0x047A, "\u{47b}"), + (0x047C, "\u{47d}"), + (0x047E, "\u{47f}"), + (0x0480, "\u{481}"), + (0x048A, "\u{48b}"), + (0x048C, "\u{48d}"), + (0x048E, "\u{48f}"), + (0x0490, "\u{491}"), + (0x0492, "\u{493}"), + (0x0494, "\u{495}"), + (0x0496, "\u{497}"), + (0x0498, "\u{499}"), + (0x049A, "\u{49b}"), + (0x049C, "\u{49d}"), + (0x049E, "\u{49f}"), + (0x04A0, "\u{4a1}"), + (0x04A2, "\u{4a3}"), + (0x04A4, "\u{4a5}"), + (0x04A6, "\u{4a7}"), + (0x04A8, "\u{4a9}"), + (0x04AA, "\u{4ab}"), + (0x04AC, "\u{4ad}"), + (0x04AE, "\u{4af}"), + (0x04B0, "\u{4b1}"), + (0x04B2, "\u{4b3}"), + (0x04B4, "\u{4b5}"), + (0x04B6, "\u{4b7}"), + (0x04B8, "\u{4b9}"), + (0x04BA, "\u{4bb}"), + (0x04BC, "\u{4bd}"), + (0x04BE, "\u{4bf}"), + (0x04C0, "\u{4cf}"), + (0x04C1, "\u{4c2}"), + (0x04C3, "\u{4c4}"), + (0x04C5, "\u{4c6}"), + (0x04C7, "\u{4c8}"), + (0x04C9, "\u{4ca}"), + (0x04CB, "\u{4cc}"), + (0x04CD, "\u{4ce}"), + (0x04D0, "\u{4d1}"), + (0x04D2, "\u{4d3}"), + (0x04D4, "\u{4d5}"), + (0x04D6, "\u{4d7}"), + (0x04D8, "\u{4d9}"), + (0x04DA, "\u{4db}"), + (0x04DC, "\u{4dd}"), + (0x04DE, "\u{4df}"), + (0x04E0, "\u{4e1}"), + (0x04E2, "\u{4e3}"), + (0x04E4, "\u{4e5}"), + (0x04E6, "\u{4e7}"), + (0x04E8, "\u{4e9}"), + (0x04EA, "\u{4eb}"), + (0x04EC, "\u{4ed}"), + (0x04EE, "\u{4ef}"), + (0x04F0, "\u{4f1}"), + (0x04F2, "\u{4f3}"), + (0x04F4, "\u{4f5}"), + (0x04F6, "\u{4f7}"), + (0x04F8, "\u{4f9}"), + (0x04FA, "\u{4fb}"), + (0x04FC, "\u{4fd}"), + (0x04FE, "\u{4ff}"), + (0x0500, "\u{501}"), + (0x0502, "\u{503}"), + (0x0504, "\u{505}"), + (0x0506, "\u{507}"), + (0x0508, "\u{509}"), + (0x050A, "\u{50b}"), + (0x050C, "\u{50d}"), + (0x050E, "\u{50f}"), + (0x0510, "\u{511}"), + (0x0512, "\u{513}"), + (0x0514, "\u{515}"), + (0x0516, "\u{517}"), + (0x0518, "\u{519}"), + (0x051A, "\u{51b}"), + (0x051C, "\u{51d}"), + (0x051E, "\u{51f}"), + (0x0520, "\u{521}"), + (0x0522, "\u{523}"), + (0x0524, "\u{525}"), + (0x0526, "\u{527}"), + (0x0528, "\u{529}"), + (0x052A, "\u{52b}"), + (0x052C, "\u{52d}"), + (0x052E, "\u{52f}"), + (0x0531, "\u{561}"), + (0x0532, "\u{562}"), + (0x0533, "\u{563}"), + (0x0534, "\u{564}"), + (0x0535, "\u{565}"), + (0x0536, "\u{566}"), + (0x0537, "\u{567}"), + (0x0538, "\u{568}"), + (0x0539, "\u{569}"), + (0x053A, "\u{56a}"), + (0x053B, "\u{56b}"), + (0x053C, "\u{56c}"), + (0x053D, "\u{56d}"), + (0x053E, "\u{56e}"), + (0x053F, "\u{56f}"), + (0x0540, "\u{570}"), + (0x0541, "\u{571}"), + (0x0542, "\u{572}"), + (0x0543, "\u{573}"), + (0x0544, "\u{574}"), + (0x0545, "\u{575}"), + (0x0546, "\u{576}"), + (0x0547, "\u{577}"), + (0x0548, "\u{578}"), + (0x0549, "\u{579}"), + (0x054A, "\u{57a}"), + (0x054B, "\u{57b}"), + (0x054C, "\u{57c}"), + (0x054D, "\u{57d}"), + (0x054E, "\u{57e}"), + (0x054F, "\u{57f}"), + (0x0550, "\u{580}"), + (0x0551, "\u{581}"), + (0x0552, "\u{582}"), + (0x0553, "\u{583}"), + (0x0554, "\u{584}"), + (0x0555, "\u{585}"), + (0x0556, "\u{586}"), + (0x0587, "\u{565}\u{582}"), + (0x10A0, "\u{2d00}"), + (0x10A1, "\u{2d01}"), + (0x10A2, "\u{2d02}"), + (0x10A3, "\u{2d03}"), + (0x10A4, "\u{2d04}"), + (0x10A5, "\u{2d05}"), + (0x10A6, "\u{2d06}"), + (0x10A7, "\u{2d07}"), + (0x10A8, "\u{2d08}"), + (0x10A9, "\u{2d09}"), + (0x10AA, "\u{2d0a}"), + (0x10AB, "\u{2d0b}"), + (0x10AC, "\u{2d0c}"), + (0x10AD, "\u{2d0d}"), + (0x10AE, "\u{2d0e}"), + (0x10AF, "\u{2d0f}"), + (0x10B0, "\u{2d10}"), + (0x10B1, "\u{2d11}"), + (0x10B2, "\u{2d12}"), + (0x10B3, "\u{2d13}"), + (0x10B4, "\u{2d14}"), + (0x10B5, "\u{2d15}"), + (0x10B6, "\u{2d16}"), + (0x10B7, "\u{2d17}"), + (0x10B8, "\u{2d18}"), + (0x10B9, "\u{2d19}"), + (0x10BA, "\u{2d1a}"), + (0x10BB, "\u{2d1b}"), + (0x10BC, "\u{2d1c}"), + (0x10BD, "\u{2d1d}"), + (0x10BE, "\u{2d1e}"), + (0x10BF, "\u{2d1f}"), + (0x10C0, "\u{2d20}"), + (0x10C1, "\u{2d21}"), + (0x10C2, "\u{2d22}"), + (0x10C3, "\u{2d23}"), + (0x10C4, "\u{2d24}"), + (0x10C5, "\u{2d25}"), + (0x10C7, "\u{2d27}"), + (0x10CD, "\u{2d2d}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{432}"), + (0x1C81, "\u{434}"), + (0x1C82, "\u{43e}"), + (0x1C83, "\u{441}"), + (0x1C84, "\u{442}"), + (0x1C85, "\u{442}"), + (0x1C86, "\u{44a}"), + (0x1C87, "\u{463}"), + (0x1C88, "\u{a64b}"), + (0x1C89, "\u{1c8a}"), + (0x1C90, "\u{10d0}"), + (0x1C91, "\u{10d1}"), + (0x1C92, "\u{10d2}"), + (0x1C93, "\u{10d3}"), + (0x1C94, "\u{10d4}"), + (0x1C95, "\u{10d5}"), + (0x1C96, "\u{10d6}"), + (0x1C97, "\u{10d7}"), + (0x1C98, "\u{10d8}"), + (0x1C99, "\u{10d9}"), + (0x1C9A, "\u{10da}"), + (0x1C9B, "\u{10db}"), + (0x1C9C, "\u{10dc}"), + (0x1C9D, "\u{10dd}"), + (0x1C9E, "\u{10de}"), + (0x1C9F, "\u{10df}"), + (0x1CA0, "\u{10e0}"), + (0x1CA1, "\u{10e1}"), + (0x1CA2, "\u{10e2}"), + (0x1CA3, "\u{10e3}"), + (0x1CA4, "\u{10e4}"), + (0x1CA5, "\u{10e5}"), + (0x1CA6, "\u{10e6}"), + (0x1CA7, "\u{10e7}"), + (0x1CA8, "\u{10e8}"), + (0x1CA9, "\u{10e9}"), + (0x1CAA, "\u{10ea}"), + (0x1CAB, "\u{10eb}"), + (0x1CAC, "\u{10ec}"), + (0x1CAD, "\u{10ed}"), + (0x1CAE, "\u{10ee}"), + (0x1CAF, "\u{10ef}"), + (0x1CB0, "\u{10f0}"), + (0x1CB1, "\u{10f1}"), + (0x1CB2, "\u{10f2}"), + (0x1CB3, "\u{10f3}"), + (0x1CB4, "\u{10f4}"), + (0x1CB5, "\u{10f5}"), + (0x1CB6, "\u{10f6}"), + (0x1CB7, "\u{10f7}"), + (0x1CB8, "\u{10f8}"), + (0x1CB9, "\u{10f9}"), + (0x1CBA, "\u{10fa}"), + (0x1CBD, "\u{10fd}"), + (0x1CBE, "\u{10fe}"), + (0x1CBF, "\u{10ff}"), + (0x1E00, "\u{1e01}"), + (0x1E02, "\u{1e03}"), + (0x1E04, "\u{1e05}"), + (0x1E06, "\u{1e07}"), + (0x1E08, "\u{1e09}"), + (0x1E0A, "\u{1e0b}"), + (0x1E0C, "\u{1e0d}"), + (0x1E0E, "\u{1e0f}"), + (0x1E10, "\u{1e11}"), + (0x1E12, "\u{1e13}"), + (0x1E14, "\u{1e15}"), + (0x1E16, "\u{1e17}"), + (0x1E18, "\u{1e19}"), + (0x1E1A, "\u{1e1b}"), + (0x1E1C, "\u{1e1d}"), + (0x1E1E, "\u{1e1f}"), + (0x1E20, "\u{1e21}"), + (0x1E22, "\u{1e23}"), + (0x1E24, "\u{1e25}"), + (0x1E26, "\u{1e27}"), + (0x1E28, "\u{1e29}"), + (0x1E2A, "\u{1e2b}"), + (0x1E2C, "\u{1e2d}"), + (0x1E2E, "\u{1e2f}"), + (0x1E30, "\u{1e31}"), + (0x1E32, "\u{1e33}"), + (0x1E34, "\u{1e35}"), + (0x1E36, "\u{1e37}"), + (0x1E38, "\u{1e39}"), + (0x1E3A, "\u{1e3b}"), + (0x1E3C, "\u{1e3d}"), + (0x1E3E, "\u{1e3f}"), + (0x1E40, "\u{1e41}"), + (0x1E42, "\u{1e43}"), + (0x1E44, "\u{1e45}"), + (0x1E46, "\u{1e47}"), + (0x1E48, "\u{1e49}"), + (0x1E4A, "\u{1e4b}"), + (0x1E4C, "\u{1e4d}"), + (0x1E4E, "\u{1e4f}"), + (0x1E50, "\u{1e51}"), + (0x1E52, "\u{1e53}"), + (0x1E54, "\u{1e55}"), + (0x1E56, "\u{1e57}"), + (0x1E58, "\u{1e59}"), + (0x1E5A, "\u{1e5b}"), + (0x1E5C, "\u{1e5d}"), + (0x1E5E, "\u{1e5f}"), + (0x1E60, "\u{1e61}"), + (0x1E62, "\u{1e63}"), + (0x1E64, "\u{1e65}"), + (0x1E66, "\u{1e67}"), + (0x1E68, "\u{1e69}"), + (0x1E6A, "\u{1e6b}"), + (0x1E6C, "\u{1e6d}"), + (0x1E6E, "\u{1e6f}"), + (0x1E70, "\u{1e71}"), + (0x1E72, "\u{1e73}"), + (0x1E74, "\u{1e75}"), + (0x1E76, "\u{1e77}"), + (0x1E78, "\u{1e79}"), + (0x1E7A, "\u{1e7b}"), + (0x1E7C, "\u{1e7d}"), + (0x1E7E, "\u{1e7f}"), + (0x1E80, "\u{1e81}"), + (0x1E82, "\u{1e83}"), + (0x1E84, "\u{1e85}"), + (0x1E86, "\u{1e87}"), + (0x1E88, "\u{1e89}"), + (0x1E8A, "\u{1e8b}"), + (0x1E8C, "\u{1e8d}"), + (0x1E8E, "\u{1e8f}"), + (0x1E90, "\u{1e91}"), + (0x1E92, "\u{1e93}"), + (0x1E94, "\u{1e95}"), + (0x1E96, "\u{68}\u{331}"), + (0x1E97, "\u{74}\u{308}"), + (0x1E98, "\u{77}\u{30a}"), + (0x1E99, "\u{79}\u{30a}"), + (0x1E9A, "\u{61}\u{2be}"), + (0x1E9B, "\u{1e61}"), + (0x1E9E, "\u{73}\u{73}"), + (0x1EA0, "\u{1ea1}"), + (0x1EA2, "\u{1ea3}"), + (0x1EA4, "\u{1ea5}"), + (0x1EA6, "\u{1ea7}"), + (0x1EA8, "\u{1ea9}"), + (0x1EAA, "\u{1eab}"), + (0x1EAC, "\u{1ead}"), + (0x1EAE, "\u{1eaf}"), + (0x1EB0, "\u{1eb1}"), + (0x1EB2, "\u{1eb3}"), + (0x1EB4, "\u{1eb5}"), + (0x1EB6, "\u{1eb7}"), + (0x1EB8, "\u{1eb9}"), + (0x1EBA, "\u{1ebb}"), + (0x1EBC, "\u{1ebd}"), + (0x1EBE, "\u{1ebf}"), + (0x1EC0, "\u{1ec1}"), + (0x1EC2, "\u{1ec3}"), + (0x1EC4, "\u{1ec5}"), + (0x1EC6, "\u{1ec7}"), + (0x1EC8, "\u{1ec9}"), + (0x1ECA, "\u{1ecb}"), + (0x1ECC, "\u{1ecd}"), + (0x1ECE, "\u{1ecf}"), + (0x1ED0, "\u{1ed1}"), + (0x1ED2, "\u{1ed3}"), + (0x1ED4, "\u{1ed5}"), + (0x1ED6, "\u{1ed7}"), + (0x1ED8, "\u{1ed9}"), + (0x1EDA, "\u{1edb}"), + (0x1EDC, "\u{1edd}"), + (0x1EDE, "\u{1edf}"), + (0x1EE0, "\u{1ee1}"), + (0x1EE2, "\u{1ee3}"), + (0x1EE4, "\u{1ee5}"), + (0x1EE6, "\u{1ee7}"), + (0x1EE8, "\u{1ee9}"), + (0x1EEA, "\u{1eeb}"), + (0x1EEC, "\u{1eed}"), + (0x1EEE, "\u{1eef}"), + (0x1EF0, "\u{1ef1}"), + (0x1EF2, "\u{1ef3}"), + (0x1EF4, "\u{1ef5}"), + (0x1EF6, "\u{1ef7}"), + (0x1EF8, "\u{1ef9}"), + (0x1EFA, "\u{1efb}"), + (0x1EFC, "\u{1efd}"), + (0x1EFE, "\u{1eff}"), + (0x1F08, "\u{1f00}"), + (0x1F09, "\u{1f01}"), + (0x1F0A, "\u{1f02}"), + (0x1F0B, "\u{1f03}"), + (0x1F0C, "\u{1f04}"), + (0x1F0D, "\u{1f05}"), + (0x1F0E, "\u{1f06}"), + (0x1F0F, "\u{1f07}"), + (0x1F18, "\u{1f10}"), + (0x1F19, "\u{1f11}"), + (0x1F1A, "\u{1f12}"), + (0x1F1B, "\u{1f13}"), + (0x1F1C, "\u{1f14}"), + (0x1F1D, "\u{1f15}"), + (0x1F28, "\u{1f20}"), + (0x1F29, "\u{1f21}"), + (0x1F2A, "\u{1f22}"), + (0x1F2B, "\u{1f23}"), + (0x1F2C, "\u{1f24}"), + (0x1F2D, "\u{1f25}"), + (0x1F2E, "\u{1f26}"), + (0x1F2F, "\u{1f27}"), + (0x1F38, "\u{1f30}"), + (0x1F39, "\u{1f31}"), + (0x1F3A, "\u{1f32}"), + (0x1F3B, "\u{1f33}"), + (0x1F3C, "\u{1f34}"), + (0x1F3D, "\u{1f35}"), + (0x1F3E, "\u{1f36}"), + (0x1F3F, "\u{1f37}"), + (0x1F48, "\u{1f40}"), + (0x1F49, "\u{1f41}"), + (0x1F4A, "\u{1f42}"), + (0x1F4B, "\u{1f43}"), + (0x1F4C, "\u{1f44}"), + (0x1F4D, "\u{1f45}"), + (0x1F50, "\u{3c5}\u{313}"), + (0x1F52, "\u{3c5}\u{313}\u{300}"), + (0x1F54, "\u{3c5}\u{313}\u{301}"), + (0x1F56, "\u{3c5}\u{313}\u{342}"), + (0x1F59, "\u{1f51}"), + (0x1F5B, "\u{1f53}"), + (0x1F5D, "\u{1f55}"), + (0x1F5F, "\u{1f57}"), + (0x1F68, "\u{1f60}"), + (0x1F69, "\u{1f61}"), + (0x1F6A, "\u{1f62}"), + (0x1F6B, "\u{1f63}"), + (0x1F6C, "\u{1f64}"), + (0x1F6D, "\u{1f65}"), + (0x1F6E, "\u{1f66}"), + (0x1F6F, "\u{1f67}"), + (0x1F80, "\u{1f00}\u{3b9}"), + (0x1F81, "\u{1f01}\u{3b9}"), + (0x1F82, "\u{1f02}\u{3b9}"), + (0x1F83, "\u{1f03}\u{3b9}"), + (0x1F84, "\u{1f04}\u{3b9}"), + (0x1F85, "\u{1f05}\u{3b9}"), + (0x1F86, "\u{1f06}\u{3b9}"), + (0x1F87, "\u{1f07}\u{3b9}"), + (0x1F88, "\u{1f00}\u{3b9}"), + (0x1F89, "\u{1f01}\u{3b9}"), + (0x1F8A, "\u{1f02}\u{3b9}"), + (0x1F8B, "\u{1f03}\u{3b9}"), + (0x1F8C, "\u{1f04}\u{3b9}"), + (0x1F8D, "\u{1f05}\u{3b9}"), + (0x1F8E, "\u{1f06}\u{3b9}"), + (0x1F8F, "\u{1f07}\u{3b9}"), + (0x1F90, "\u{1f20}\u{3b9}"), + (0x1F91, "\u{1f21}\u{3b9}"), + (0x1F92, "\u{1f22}\u{3b9}"), + (0x1F93, "\u{1f23}\u{3b9}"), + (0x1F94, "\u{1f24}\u{3b9}"), + (0x1F95, "\u{1f25}\u{3b9}"), + (0x1F96, "\u{1f26}\u{3b9}"), + (0x1F97, "\u{1f27}\u{3b9}"), + (0x1F98, "\u{1f20}\u{3b9}"), + (0x1F99, "\u{1f21}\u{3b9}"), + (0x1F9A, "\u{1f22}\u{3b9}"), + (0x1F9B, "\u{1f23}\u{3b9}"), + (0x1F9C, "\u{1f24}\u{3b9}"), + (0x1F9D, "\u{1f25}\u{3b9}"), + (0x1F9E, "\u{1f26}\u{3b9}"), + (0x1F9F, "\u{1f27}\u{3b9}"), + (0x1FA0, "\u{1f60}\u{3b9}"), + (0x1FA1, "\u{1f61}\u{3b9}"), + (0x1FA2, "\u{1f62}\u{3b9}"), + (0x1FA3, "\u{1f63}\u{3b9}"), + (0x1FA4, "\u{1f64}\u{3b9}"), + (0x1FA5, "\u{1f65}\u{3b9}"), + (0x1FA6, "\u{1f66}\u{3b9}"), + (0x1FA7, "\u{1f67}\u{3b9}"), + (0x1FA8, "\u{1f60}\u{3b9}"), + (0x1FA9, "\u{1f61}\u{3b9}"), + (0x1FAA, "\u{1f62}\u{3b9}"), + (0x1FAB, "\u{1f63}\u{3b9}"), + (0x1FAC, "\u{1f64}\u{3b9}"), + (0x1FAD, "\u{1f65}\u{3b9}"), + (0x1FAE, "\u{1f66}\u{3b9}"), + (0x1FAF, "\u{1f67}\u{3b9}"), + (0x1FB2, "\u{1f70}\u{3b9}"), + (0x1FB3, "\u{3b1}\u{3b9}"), + (0x1FB4, "\u{3ac}\u{3b9}"), + (0x1FB6, "\u{3b1}\u{342}"), + (0x1FB7, "\u{3b1}\u{342}\u{3b9}"), + (0x1FB8, "\u{1fb0}"), + (0x1FB9, "\u{1fb1}"), + (0x1FBA, "\u{1f70}"), + (0x1FBB, "\u{1f71}"), + (0x1FBC, "\u{3b1}\u{3b9}"), + (0x1FBE, "\u{3b9}"), + (0x1FC2, "\u{1f74}\u{3b9}"), + (0x1FC3, "\u{3b7}\u{3b9}"), + (0x1FC4, "\u{3ae}\u{3b9}"), + (0x1FC6, "\u{3b7}\u{342}"), + (0x1FC7, "\u{3b7}\u{342}\u{3b9}"), + (0x1FC8, "\u{1f72}"), + (0x1FC9, "\u{1f73}"), + (0x1FCA, "\u{1f74}"), + (0x1FCB, "\u{1f75}"), + (0x1FCC, "\u{3b7}\u{3b9}"), + (0x1FD2, "\u{3b9}\u{308}\u{300}"), + (0x1FD3, "\u{3b9}\u{308}\u{301}"), + (0x1FD6, "\u{3b9}\u{342}"), + (0x1FD7, "\u{3b9}\u{308}\u{342}"), + (0x1FD8, "\u{1fd0}"), + (0x1FD9, "\u{1fd1}"), + (0x1FDA, "\u{1f76}"), + (0x1FDB, "\u{1f77}"), + (0x1FE2, "\u{3c5}\u{308}\u{300}"), + (0x1FE3, "\u{3c5}\u{308}\u{301}"), + (0x1FE4, "\u{3c1}\u{313}"), + (0x1FE6, "\u{3c5}\u{342}"), + (0x1FE7, "\u{3c5}\u{308}\u{342}"), + (0x1FE8, "\u{1fe0}"), + (0x1FE9, "\u{1fe1}"), + (0x1FEA, "\u{1f7a}"), + (0x1FEB, "\u{1f7b}"), + (0x1FEC, "\u{1fe5}"), + (0x1FF2, "\u{1f7c}\u{3b9}"), + (0x1FF3, "\u{3c9}\u{3b9}"), + (0x1FF4, "\u{3ce}\u{3b9}"), + (0x1FF6, "\u{3c9}\u{342}"), + (0x1FF7, "\u{3c9}\u{342}\u{3b9}"), + (0x1FF8, "\u{1f78}"), + (0x1FF9, "\u{1f79}"), + (0x1FFA, "\u{1f7c}"), + (0x1FFB, "\u{1f7d}"), + (0x1FFC, "\u{3c9}\u{3b9}"), + (0x2126, "\u{3c9}"), + (0x212A, "\u{6b}"), + (0x212B, "\u{e5}"), + (0x2132, "\u{214e}"), + (0x2160, "\u{2170}"), + (0x2161, "\u{2171}"), + (0x2162, "\u{2172}"), + (0x2163, "\u{2173}"), + (0x2164, "\u{2174}"), + (0x2165, "\u{2175}"), + (0x2166, "\u{2176}"), + (0x2167, "\u{2177}"), + (0x2168, "\u{2178}"), + (0x2169, "\u{2179}"), + (0x216A, "\u{217a}"), + (0x216B, "\u{217b}"), + (0x216C, "\u{217c}"), + (0x216D, "\u{217d}"), + (0x216E, "\u{217e}"), + (0x216F, "\u{217f}"), + (0x2183, "\u{2184}"), + (0x24B6, "\u{24d0}"), + (0x24B7, "\u{24d1}"), + (0x24B8, "\u{24d2}"), + (0x24B9, "\u{24d3}"), + (0x24BA, "\u{24d4}"), + (0x24BB, "\u{24d5}"), + (0x24BC, "\u{24d6}"), + (0x24BD, "\u{24d7}"), + (0x24BE, "\u{24d8}"), + (0x24BF, "\u{24d9}"), + (0x24C0, "\u{24da}"), + (0x24C1, "\u{24db}"), + (0x24C2, "\u{24dc}"), + (0x24C3, "\u{24dd}"), + (0x24C4, "\u{24de}"), + (0x24C5, "\u{24df}"), + (0x24C6, "\u{24e0}"), + (0x24C7, "\u{24e1}"), + (0x24C8, "\u{24e2}"), + (0x24C9, "\u{24e3}"), + (0x24CA, "\u{24e4}"), + (0x24CB, "\u{24e5}"), + (0x24CC, "\u{24e6}"), + (0x24CD, "\u{24e7}"), + (0x24CE, "\u{24e8}"), + (0x24CF, "\u{24e9}"), + (0x2C00, "\u{2c30}"), + (0x2C01, "\u{2c31}"), + (0x2C02, "\u{2c32}"), + (0x2C03, "\u{2c33}"), + (0x2C04, "\u{2c34}"), + (0x2C05, "\u{2c35}"), + (0x2C06, "\u{2c36}"), + (0x2C07, "\u{2c37}"), + (0x2C08, "\u{2c38}"), + (0x2C09, "\u{2c39}"), + (0x2C0A, "\u{2c3a}"), + (0x2C0B, "\u{2c3b}"), + (0x2C0C, "\u{2c3c}"), + (0x2C0D, "\u{2c3d}"), + (0x2C0E, "\u{2c3e}"), + (0x2C0F, "\u{2c3f}"), + (0x2C10, "\u{2c40}"), + (0x2C11, "\u{2c41}"), + (0x2C12, "\u{2c42}"), + (0x2C13, "\u{2c43}"), + (0x2C14, "\u{2c44}"), + (0x2C15, "\u{2c45}"), + (0x2C16, "\u{2c46}"), + (0x2C17, "\u{2c47}"), + (0x2C18, "\u{2c48}"), + (0x2C19, "\u{2c49}"), + (0x2C1A, "\u{2c4a}"), + (0x2C1B, "\u{2c4b}"), + (0x2C1C, "\u{2c4c}"), + (0x2C1D, "\u{2c4d}"), + (0x2C1E, "\u{2c4e}"), + (0x2C1F, "\u{2c4f}"), + (0x2C20, "\u{2c50}"), + (0x2C21, "\u{2c51}"), + (0x2C22, "\u{2c52}"), + (0x2C23, "\u{2c53}"), + (0x2C24, "\u{2c54}"), + (0x2C25, "\u{2c55}"), + (0x2C26, "\u{2c56}"), + (0x2C27, "\u{2c57}"), + (0x2C28, "\u{2c58}"), + (0x2C29, "\u{2c59}"), + (0x2C2A, "\u{2c5a}"), + (0x2C2B, "\u{2c5b}"), + (0x2C2C, "\u{2c5c}"), + (0x2C2D, "\u{2c5d}"), + (0x2C2E, "\u{2c5e}"), + (0x2C2F, "\u{2c5f}"), + (0x2C60, "\u{2c61}"), + (0x2C62, "\u{26b}"), + (0x2C63, "\u{1d7d}"), + (0x2C64, "\u{27d}"), + (0x2C67, "\u{2c68}"), + (0x2C69, "\u{2c6a}"), + (0x2C6B, "\u{2c6c}"), + (0x2C6D, "\u{251}"), + (0x2C6E, "\u{271}"), + (0x2C6F, "\u{250}"), + (0x2C70, "\u{252}"), + (0x2C72, "\u{2c73}"), + (0x2C75, "\u{2c76}"), + (0x2C7E, "\u{23f}"), + (0x2C7F, "\u{240}"), + (0x2C80, "\u{2c81}"), + (0x2C82, "\u{2c83}"), + (0x2C84, "\u{2c85}"), + (0x2C86, "\u{2c87}"), + (0x2C88, "\u{2c89}"), + (0x2C8A, "\u{2c8b}"), + (0x2C8C, "\u{2c8d}"), + (0x2C8E, "\u{2c8f}"), + (0x2C90, "\u{2c91}"), + (0x2C92, "\u{2c93}"), + (0x2C94, "\u{2c95}"), + (0x2C96, "\u{2c97}"), + (0x2C98, "\u{2c99}"), + (0x2C9A, "\u{2c9b}"), + (0x2C9C, "\u{2c9d}"), + (0x2C9E, "\u{2c9f}"), + (0x2CA0, "\u{2ca1}"), + (0x2CA2, "\u{2ca3}"), + (0x2CA4, "\u{2ca5}"), + (0x2CA6, "\u{2ca7}"), + (0x2CA8, "\u{2ca9}"), + (0x2CAA, "\u{2cab}"), + (0x2CAC, "\u{2cad}"), + (0x2CAE, "\u{2caf}"), + (0x2CB0, "\u{2cb1}"), + (0x2CB2, "\u{2cb3}"), + (0x2CB4, "\u{2cb5}"), + (0x2CB6, "\u{2cb7}"), + (0x2CB8, "\u{2cb9}"), + (0x2CBA, "\u{2cbb}"), + (0x2CBC, "\u{2cbd}"), + (0x2CBE, "\u{2cbf}"), + (0x2CC0, "\u{2cc1}"), + (0x2CC2, "\u{2cc3}"), + (0x2CC4, "\u{2cc5}"), + (0x2CC6, "\u{2cc7}"), + (0x2CC8, "\u{2cc9}"), + (0x2CCA, "\u{2ccb}"), + (0x2CCC, "\u{2ccd}"), + (0x2CCE, "\u{2ccf}"), + (0x2CD0, "\u{2cd1}"), + (0x2CD2, "\u{2cd3}"), + (0x2CD4, "\u{2cd5}"), + (0x2CD6, "\u{2cd7}"), + (0x2CD8, "\u{2cd9}"), + (0x2CDA, "\u{2cdb}"), + (0x2CDC, "\u{2cdd}"), + (0x2CDE, "\u{2cdf}"), + (0x2CE0, "\u{2ce1}"), + (0x2CE2, "\u{2ce3}"), + (0x2CEB, "\u{2cec}"), + (0x2CED, "\u{2cee}"), + (0x2CF2, "\u{2cf3}"), + (0xA640, "\u{a641}"), + (0xA642, "\u{a643}"), + (0xA644, "\u{a645}"), + (0xA646, "\u{a647}"), + (0xA648, "\u{a649}"), + (0xA64A, "\u{a64b}"), + (0xA64C, "\u{a64d}"), + (0xA64E, "\u{a64f}"), + (0xA650, "\u{a651}"), + (0xA652, "\u{a653}"), + (0xA654, "\u{a655}"), + (0xA656, "\u{a657}"), + (0xA658, "\u{a659}"), + (0xA65A, "\u{a65b}"), + (0xA65C, "\u{a65d}"), + (0xA65E, "\u{a65f}"), + (0xA660, "\u{a661}"), + (0xA662, "\u{a663}"), + (0xA664, "\u{a665}"), + (0xA666, "\u{a667}"), + (0xA668, "\u{a669}"), + (0xA66A, "\u{a66b}"), + (0xA66C, "\u{a66d}"), + (0xA680, "\u{a681}"), + (0xA682, "\u{a683}"), + (0xA684, "\u{a685}"), + (0xA686, "\u{a687}"), + (0xA688, "\u{a689}"), + (0xA68A, "\u{a68b}"), + (0xA68C, "\u{a68d}"), + (0xA68E, "\u{a68f}"), + (0xA690, "\u{a691}"), + (0xA692, "\u{a693}"), + (0xA694, "\u{a695}"), + (0xA696, "\u{a697}"), + (0xA698, "\u{a699}"), + (0xA69A, "\u{a69b}"), + (0xA722, "\u{a723}"), + (0xA724, "\u{a725}"), + (0xA726, "\u{a727}"), + (0xA728, "\u{a729}"), + (0xA72A, "\u{a72b}"), + (0xA72C, "\u{a72d}"), + (0xA72E, "\u{a72f}"), + (0xA732, "\u{a733}"), + (0xA734, "\u{a735}"), + (0xA736, "\u{a737}"), + (0xA738, "\u{a739}"), + (0xA73A, "\u{a73b}"), + (0xA73C, "\u{a73d}"), + (0xA73E, "\u{a73f}"), + (0xA740, "\u{a741}"), + (0xA742, "\u{a743}"), + (0xA744, "\u{a745}"), + (0xA746, "\u{a747}"), + (0xA748, "\u{a749}"), + (0xA74A, "\u{a74b}"), + (0xA74C, "\u{a74d}"), + (0xA74E, "\u{a74f}"), + (0xA750, "\u{a751}"), + (0xA752, "\u{a753}"), + (0xA754, "\u{a755}"), + (0xA756, "\u{a757}"), + (0xA758, "\u{a759}"), + (0xA75A, "\u{a75b}"), + (0xA75C, "\u{a75d}"), + (0xA75E, "\u{a75f}"), + (0xA760, "\u{a761}"), + (0xA762, "\u{a763}"), + (0xA764, "\u{a765}"), + (0xA766, "\u{a767}"), + (0xA768, "\u{a769}"), + (0xA76A, "\u{a76b}"), + (0xA76C, "\u{a76d}"), + (0xA76E, "\u{a76f}"), + (0xA779, "\u{a77a}"), + (0xA77B, "\u{a77c}"), + (0xA77D, "\u{1d79}"), + (0xA77E, "\u{a77f}"), + (0xA780, "\u{a781}"), + (0xA782, "\u{a783}"), + (0xA784, "\u{a785}"), + (0xA786, "\u{a787}"), + (0xA78B, "\u{a78c}"), + (0xA78D, "\u{265}"), + (0xA790, "\u{a791}"), + (0xA792, "\u{a793}"), + (0xA796, "\u{a797}"), + (0xA798, "\u{a799}"), + (0xA79A, "\u{a79b}"), + (0xA79C, "\u{a79d}"), + (0xA79E, "\u{a79f}"), + (0xA7A0, "\u{a7a1}"), + (0xA7A2, "\u{a7a3}"), + (0xA7A4, "\u{a7a5}"), + (0xA7A6, "\u{a7a7}"), + (0xA7A8, "\u{a7a9}"), + (0xA7AA, "\u{266}"), + (0xA7AB, "\u{25c}"), + (0xA7AC, "\u{261}"), + (0xA7AD, "\u{26c}"), + (0xA7AE, "\u{26a}"), + (0xA7B0, "\u{29e}"), + (0xA7B1, "\u{287}"), + (0xA7B2, "\u{29d}"), + (0xA7B3, "\u{ab53}"), + (0xA7B4, "\u{a7b5}"), + (0xA7B6, "\u{a7b7}"), + (0xA7B8, "\u{a7b9}"), + (0xA7BA, "\u{a7bb}"), + (0xA7BC, "\u{a7bd}"), + (0xA7BE, "\u{a7bf}"), + (0xA7C0, "\u{a7c1}"), + (0xA7C2, "\u{a7c3}"), + (0xA7C4, "\u{a794}"), + (0xA7C5, "\u{282}"), + (0xA7C6, "\u{1d8e}"), + (0xA7C7, "\u{a7c8}"), + (0xA7C9, "\u{a7ca}"), + (0xA7CB, "\u{264}"), + (0xA7CC, "\u{a7cd}"), + (0xA7D0, "\u{a7d1}"), + (0xA7D6, "\u{a7d7}"), + (0xA7D8, "\u{a7d9}"), + (0xA7DA, "\u{a7db}"), + (0xA7DC, "\u{19b}"), + (0xA7F5, "\u{a7f6}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{66}\u{66}"), + (0xFB01, "\u{66}\u{69}"), + (0xFB02, "\u{66}\u{6c}"), + (0xFB03, "\u{66}\u{66}\u{69}"), + (0xFB04, "\u{66}\u{66}\u{6c}"), + (0xFB05, "\u{73}\u{74}"), + (0xFB06, "\u{73}\u{74}"), + (0xFB13, "\u{574}\u{576}"), + (0xFB14, "\u{574}\u{565}"), + (0xFB15, "\u{574}\u{56b}"), + (0xFB16, "\u{57e}\u{576}"), + (0xFB17, "\u{574}\u{56d}"), + (0xFF21, "\u{ff41}"), + (0xFF22, "\u{ff42}"), + (0xFF23, "\u{ff43}"), + (0xFF24, "\u{ff44}"), + (0xFF25, "\u{ff45}"), + (0xFF26, "\u{ff46}"), + (0xFF27, "\u{ff47}"), + (0xFF28, "\u{ff48}"), + (0xFF29, "\u{ff49}"), + (0xFF2A, "\u{ff4a}"), + (0xFF2B, "\u{ff4b}"), + (0xFF2C, "\u{ff4c}"), + (0xFF2D, "\u{ff4d}"), + (0xFF2E, "\u{ff4e}"), + (0xFF2F, "\u{ff4f}"), + (0xFF30, "\u{ff50}"), + (0xFF31, "\u{ff51}"), + (0xFF32, "\u{ff52}"), + (0xFF33, "\u{ff53}"), + (0xFF34, "\u{ff54}"), + (0xFF35, "\u{ff55}"), + (0xFF36, "\u{ff56}"), + (0xFF37, "\u{ff57}"), + (0xFF38, "\u{ff58}"), + (0xFF39, "\u{ff59}"), + (0xFF3A, "\u{ff5a}"), + (0x10400, "\u{10428}"), + (0x10401, "\u{10429}"), + (0x10402, "\u{1042a}"), + (0x10403, "\u{1042b}"), + (0x10404, "\u{1042c}"), + (0x10405, "\u{1042d}"), + (0x10406, "\u{1042e}"), + (0x10407, "\u{1042f}"), + (0x10408, "\u{10430}"), + (0x10409, "\u{10431}"), + (0x1040A, "\u{10432}"), + (0x1040B, "\u{10433}"), + (0x1040C, "\u{10434}"), + (0x1040D, "\u{10435}"), + (0x1040E, "\u{10436}"), + (0x1040F, "\u{10437}"), + (0x10410, "\u{10438}"), + (0x10411, "\u{10439}"), + (0x10412, "\u{1043a}"), + (0x10413, "\u{1043b}"), + (0x10414, "\u{1043c}"), + (0x10415, "\u{1043d}"), + (0x10416, "\u{1043e}"), + (0x10417, "\u{1043f}"), + (0x10418, "\u{10440}"), + (0x10419, "\u{10441}"), + (0x1041A, "\u{10442}"), + (0x1041B, "\u{10443}"), + (0x1041C, "\u{10444}"), + (0x1041D, "\u{10445}"), + (0x1041E, "\u{10446}"), + (0x1041F, "\u{10447}"), + (0x10420, "\u{10448}"), + (0x10421, "\u{10449}"), + (0x10422, "\u{1044a}"), + (0x10423, "\u{1044b}"), + (0x10424, "\u{1044c}"), + (0x10425, "\u{1044d}"), + (0x10426, "\u{1044e}"), + (0x10427, "\u{1044f}"), + (0x104B0, "\u{104d8}"), + (0x104B1, "\u{104d9}"), + (0x104B2, "\u{104da}"), + (0x104B3, "\u{104db}"), + (0x104B4, "\u{104dc}"), + (0x104B5, "\u{104dd}"), + (0x104B6, "\u{104de}"), + (0x104B7, "\u{104df}"), + (0x104B8, "\u{104e0}"), + (0x104B9, "\u{104e1}"), + (0x104BA, "\u{104e2}"), + (0x104BB, "\u{104e3}"), + (0x104BC, "\u{104e4}"), + (0x104BD, "\u{104e5}"), + (0x104BE, "\u{104e6}"), + (0x104BF, "\u{104e7}"), + (0x104C0, "\u{104e8}"), + (0x104C1, "\u{104e9}"), + (0x104C2, "\u{104ea}"), + (0x104C3, "\u{104eb}"), + (0x104C4, "\u{104ec}"), + (0x104C5, "\u{104ed}"), + (0x104C6, "\u{104ee}"), + (0x104C7, "\u{104ef}"), + (0x104C8, "\u{104f0}"), + (0x104C9, "\u{104f1}"), + (0x104CA, "\u{104f2}"), + (0x104CB, "\u{104f3}"), + (0x104CC, "\u{104f4}"), + (0x104CD, "\u{104f5}"), + (0x104CE, "\u{104f6}"), + (0x104CF, "\u{104f7}"), + (0x104D0, "\u{104f8}"), + (0x104D1, "\u{104f9}"), + (0x104D2, "\u{104fa}"), + (0x104D3, "\u{104fb}"), + (0x10570, "\u{10597}"), + (0x10571, "\u{10598}"), + (0x10572, "\u{10599}"), + (0x10573, "\u{1059a}"), + (0x10574, "\u{1059b}"), + (0x10575, "\u{1059c}"), + (0x10576, "\u{1059d}"), + (0x10577, "\u{1059e}"), + (0x10578, "\u{1059f}"), + (0x10579, "\u{105a0}"), + (0x1057A, "\u{105a1}"), + (0x1057C, "\u{105a3}"), + (0x1057D, "\u{105a4}"), + (0x1057E, "\u{105a5}"), + (0x1057F, "\u{105a6}"), + (0x10580, "\u{105a7}"), + (0x10581, "\u{105a8}"), + (0x10582, "\u{105a9}"), + (0x10583, "\u{105aa}"), + (0x10584, "\u{105ab}"), + (0x10585, "\u{105ac}"), + (0x10586, "\u{105ad}"), + (0x10587, "\u{105ae}"), + (0x10588, "\u{105af}"), + (0x10589, "\u{105b0}"), + (0x1058A, "\u{105b1}"), + (0x1058C, "\u{105b3}"), + (0x1058D, "\u{105b4}"), + (0x1058E, "\u{105b5}"), + (0x1058F, "\u{105b6}"), + (0x10590, "\u{105b7}"), + (0x10591, "\u{105b8}"), + (0x10592, "\u{105b9}"), + (0x10594, "\u{105bb}"), + (0x10595, "\u{105bc}"), + (0x10C80, "\u{10cc0}"), + (0x10C81, "\u{10cc1}"), + (0x10C82, "\u{10cc2}"), + (0x10C83, "\u{10cc3}"), + (0x10C84, "\u{10cc4}"), + (0x10C85, "\u{10cc5}"), + (0x10C86, "\u{10cc6}"), + (0x10C87, "\u{10cc7}"), + (0x10C88, "\u{10cc8}"), + (0x10C89, "\u{10cc9}"), + (0x10C8A, "\u{10cca}"), + (0x10C8B, "\u{10ccb}"), + (0x10C8C, "\u{10ccc}"), + (0x10C8D, "\u{10ccd}"), + (0x10C8E, "\u{10cce}"), + (0x10C8F, "\u{10ccf}"), + (0x10C90, "\u{10cd0}"), + (0x10C91, "\u{10cd1}"), + (0x10C92, "\u{10cd2}"), + (0x10C93, "\u{10cd3}"), + (0x10C94, "\u{10cd4}"), + (0x10C95, "\u{10cd5}"), + (0x10C96, "\u{10cd6}"), + (0x10C97, "\u{10cd7}"), + (0x10C98, "\u{10cd8}"), + (0x10C99, "\u{10cd9}"), + (0x10C9A, "\u{10cda}"), + (0x10C9B, "\u{10cdb}"), + (0x10C9C, "\u{10cdc}"), + (0x10C9D, "\u{10cdd}"), + (0x10C9E, "\u{10cde}"), + (0x10C9F, "\u{10cdf}"), + (0x10CA0, "\u{10ce0}"), + (0x10CA1, "\u{10ce1}"), + (0x10CA2, "\u{10ce2}"), + (0x10CA3, "\u{10ce3}"), + (0x10CA4, "\u{10ce4}"), + (0x10CA5, "\u{10ce5}"), + (0x10CA6, "\u{10ce6}"), + (0x10CA7, "\u{10ce7}"), + (0x10CA8, "\u{10ce8}"), + (0x10CA9, "\u{10ce9}"), + (0x10CAA, "\u{10cea}"), + (0x10CAB, "\u{10ceb}"), + (0x10CAC, "\u{10cec}"), + (0x10CAD, "\u{10ced}"), + (0x10CAE, "\u{10cee}"), + (0x10CAF, "\u{10cef}"), + (0x10CB0, "\u{10cf0}"), + (0x10CB1, "\u{10cf1}"), + (0x10CB2, "\u{10cf2}"), + (0x10D50, "\u{10d70}"), + (0x10D51, "\u{10d71}"), + (0x10D52, "\u{10d72}"), + (0x10D53, "\u{10d73}"), + (0x10D54, "\u{10d74}"), + (0x10D55, "\u{10d75}"), + (0x10D56, "\u{10d76}"), + (0x10D57, "\u{10d77}"), + (0x10D58, "\u{10d78}"), + (0x10D59, "\u{10d79}"), + (0x10D5A, "\u{10d7a}"), + (0x10D5B, "\u{10d7b}"), + (0x10D5C, "\u{10d7c}"), + (0x10D5D, "\u{10d7d}"), + (0x10D5E, "\u{10d7e}"), + (0x10D5F, "\u{10d7f}"), + (0x10D60, "\u{10d80}"), + (0x10D61, "\u{10d81}"), + (0x10D62, "\u{10d82}"), + (0x10D63, "\u{10d83}"), + (0x10D64, "\u{10d84}"), + (0x10D65, "\u{10d85}"), + (0x118A0, "\u{118c0}"), + (0x118A1, "\u{118c1}"), + (0x118A2, "\u{118c2}"), + (0x118A3, "\u{118c3}"), + (0x118A4, "\u{118c4}"), + (0x118A5, "\u{118c5}"), + (0x118A6, "\u{118c6}"), + (0x118A7, "\u{118c7}"), + (0x118A8, "\u{118c8}"), + (0x118A9, "\u{118c9}"), + (0x118AA, "\u{118ca}"), + (0x118AB, "\u{118cb}"), + (0x118AC, "\u{118cc}"), + (0x118AD, "\u{118cd}"), + (0x118AE, "\u{118ce}"), + (0x118AF, "\u{118cf}"), + (0x118B0, "\u{118d0}"), + (0x118B1, "\u{118d1}"), + (0x118B2, "\u{118d2}"), + (0x118B3, "\u{118d3}"), + (0x118B4, "\u{118d4}"), + (0x118B5, "\u{118d5}"), + (0x118B6, "\u{118d6}"), + (0x118B7, "\u{118d7}"), + (0x118B8, "\u{118d8}"), + (0x118B9, "\u{118d9}"), + (0x118BA, "\u{118da}"), + (0x118BB, "\u{118db}"), + (0x118BC, "\u{118dc}"), + (0x118BD, "\u{118dd}"), + (0x118BE, "\u{118de}"), + (0x118BF, "\u{118df}"), + (0x16E40, "\u{16e60}"), + (0x16E41, "\u{16e61}"), + (0x16E42, "\u{16e62}"), + (0x16E43, "\u{16e63}"), + (0x16E44, "\u{16e64}"), + (0x16E45, "\u{16e65}"), + (0x16E46, "\u{16e66}"), + (0x16E47, "\u{16e67}"), + (0x16E48, "\u{16e68}"), + (0x16E49, "\u{16e69}"), + (0x16E4A, "\u{16e6a}"), + (0x16E4B, "\u{16e6b}"), + (0x16E4C, "\u{16e6c}"), + (0x16E4D, "\u{16e6d}"), + (0x16E4E, "\u{16e6e}"), + (0x16E4F, "\u{16e6f}"), + (0x16E50, "\u{16e70}"), + (0x16E51, "\u{16e71}"), + (0x16E52, "\u{16e72}"), + (0x16E53, "\u{16e73}"), + (0x16E54, "\u{16e74}"), + (0x16E55, "\u{16e75}"), + (0x16E56, "\u{16e76}"), + (0x16E57, "\u{16e77}"), + (0x16E58, "\u{16e78}"), + (0x16E59, "\u{16e79}"), + (0x16E5A, "\u{16e7a}"), + (0x16E5B, "\u{16e7b}"), + (0x16E5C, "\u{16e7c}"), + (0x16E5D, "\u{16e7d}"), + (0x16E5E, "\u{16e7e}"), + (0x16E5F, "\u{16e7f}"), + (0x1E900, "\u{1e922}"), + (0x1E901, "\u{1e923}"), + (0x1E902, "\u{1e924}"), + (0x1E903, "\u{1e925}"), + (0x1E904, "\u{1e926}"), + (0x1E905, "\u{1e927}"), + (0x1E906, "\u{1e928}"), + (0x1E907, "\u{1e929}"), + (0x1E908, "\u{1e92a}"), + (0x1E909, "\u{1e92b}"), + (0x1E90A, "\u{1e92c}"), + (0x1E90B, "\u{1e92d}"), + (0x1E90C, "\u{1e92e}"), + (0x1E90D, "\u{1e92f}"), + (0x1E90E, "\u{1e930}"), + (0x1E90F, "\u{1e931}"), + (0x1E910, "\u{1e932}"), + (0x1E911, "\u{1e933}"), + (0x1E912, "\u{1e934}"), + (0x1E913, "\u{1e935}"), + (0x1E914, "\u{1e936}"), + (0x1E915, "\u{1e937}"), + (0x1E916, "\u{1e938}"), + (0x1E917, "\u{1e939}"), + (0x1E918, "\u{1e93a}"), + (0x1E919, "\u{1e93b}"), + (0x1E91A, "\u{1e93c}"), + (0x1E91B, "\u{1e93d}"), + (0x1E91C, "\u{1e93e}"), + (0x1E91D, "\u{1e93f}"), + (0x1E91E, "\u{1e940}"), + (0x1E91F, "\u{1e941}"), + (0x1E920, "\u{1e942}"), + (0x1E921, "\u{1e943}"), +] diff --git a/crates/sqlbuild-core/src/text/main/active_python_text.rs b/crates/sqlbuild-core/src/text/main/active_python_text.rs new file mode 100644 index 0000000000..220c7dfee5 --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/active_python_text.rs @@ -0,0 +1,27 @@ +//! The running Python's string semantics, set once by the extension module. + +use std::sync::OnceLock; + +use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; +use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, UPPER_TABLES}; +use crate::text::models::{CleandocMargin, PythonText}; + +static ACTIVE: OnceLock = OnceLock::new(); + +/// Record the running Python's semantics; later calls keep the first value. +pub fn set_active_python_text(python: PythonText) { + let _ = ACTIVE.set(python); +} + +/// The running Python's semantics; Rust-only callers that never set them get Python 3.12's. +pub fn active_python_text() -> PythonText { + *ACTIVE.get_or_init(|| PythonText { + alnum_ranges: ALNUM_TABLES[0].1, + alpha_ranges: ALPHA_TABLES[0].1, + decimal_ranges: DECIMAL_TABLES[0].1, + casefold_mappings: CASEFOLD_TABLES[0].1, + upper_mappings: UPPER_TABLES[0].1, + cleandoc_margin: CleandocMargin::Whitespace, + minor_version: 12, + }) +} diff --git a/crates/sqlbuild-core/src/text/main/mod.rs b/crates/sqlbuild-core/src/text/main/mod.rs index 2b20809e36..0353af27a4 100644 --- a/crates/sqlbuild-core/src/text/main/mod.rs +++ b/crates/sqlbuild-core/src/text/main/mod.rs @@ -1,3 +1,4 @@ +pub mod active_python_text; pub mod close_matches; pub mod decode_python_text; pub mod is_python_alnum; @@ -5,6 +6,9 @@ pub mod is_python_alpha; pub mod is_python_decimal; pub mod is_python_space; pub mod is_python_word; +pub mod python_casefold; pub mod python_cleandoc; +pub mod python_decimal_value; pub mod python_strip; pub mod python_text; +pub mod python_upper; diff --git a/crates/sqlbuild-core/src/text/main/python_casefold.rs b/crates/sqlbuild-core/src/text/main/python_casefold.rs new file mode 100644 index 0000000000..7e014666dc --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/python_casefold.rs @@ -0,0 +1,12 @@ +//! Python's `str.casefold()`, from a table generated by Python. + +use crate::text::_helpers::case_tables::mapped; +use crate::text::models::PythonText; + +/// Python's `text.casefold()`. +pub fn python_casefold(python: PythonText, text: &str) -> String { + if text.is_ascii() { + return text.to_ascii_lowercase(); + } + mapped(python.casefold_mappings, text) +} diff --git a/crates/sqlbuild-core/src/text/main/python_decimal_value.rs b/crates/sqlbuild-core/src/text/main/python_decimal_value.rs new file mode 100644 index 0000000000..0bad0311e8 --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/python_decimal_value.rs @@ -0,0 +1,21 @@ +//! Python's `unicodedata.decimal` for the characters `str.isdecimal()` accepts. + +use crate::text::models::PythonText; + +/// The digit value Python's `int()` reads from one character, if `str.isdecimal()` is true. +/// +/// Every generated decimal range starts at a zero digit and runs in blocks of ten. +pub fn python_decimal_value(python: PythonText, character: char) -> Option { + if character.is_ascii() { + return character.to_digit(10); + } + let code_point: u32 = u32::from(character); + let index: usize = python + .decimal_ranges + .partition_point(|(_, end)| *end < code_point); + let (start, _) = python + .decimal_ranges + .get(index) + .filter(|(start, _)| *start <= code_point)?; + Some((code_point - start) % 10) +} diff --git a/crates/sqlbuild-core/src/text/main/python_text.rs b/crates/sqlbuild-core/src/text/main/python_text.rs index f8d2bcfcdf..8b6c8afdf7 100644 --- a/crates/sqlbuild-core/src/text/main/python_text.rs +++ b/crates/sqlbuild-core/src/text/main/python_text.rs @@ -1,6 +1,7 @@ //! Select the Python string semantics of one CPython release. use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; +use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, UPPER_TABLES}; use crate::text::models::{CleandocMargin, PythonText}; /// The semantics of one Python and its `unicodedata.unidata_version`, or `None` if unknown. @@ -22,8 +23,18 @@ pub fn python_text(python_version: (u8, u8), unicode_version: &str) -> Option String { + if text.is_ascii() { + return text.to_ascii_uppercase(); + } + mapped(python.upper_mappings, text) +} diff --git a/crates/sqlbuild-core/src/text/models.rs b/crates/sqlbuild-core/src/text/models.rs index 037d0c7657..31bdf25aa0 100644 --- a/crates/sqlbuild-core/src/text/models.rs +++ b/crates/sqlbuild-core/src/text/models.rs @@ -109,6 +109,10 @@ pub struct PythonText { pub(crate) alpha_ranges: &'static [(u32, u32)], /// Inclusive code point ranges where `str.isdecimal()` (regex `\d`) is true, in order. pub(crate) decimal_ranges: &'static [(u32, u32)], + /// Every character `str.casefold()` changes, with its folding, in code point order. + pub(crate) casefold_mappings: &'static [(u32, &'static str)], + /// Every character `str.upper()` changes, with its upper case, in code point order. + pub(crate) upper_mappings: &'static [(u32, &'static str)], pub(crate) cleandoc_margin: CleandocMargin, /// The CPython minor version, for library behaviour that changed between 3.x releases. pub(crate) minor_version: u8, diff --git a/crates/sqlbuild-core/src/text/tests/test_python_compat.rs b/crates/sqlbuild-core/src/text/tests/test_python_compat.rs index e09e2ec546..5fdcb57067 100644 --- a/crates/sqlbuild-core/src/text/tests/test_python_compat.rs +++ b/crates/sqlbuild-core/src/text/tests/test_python_compat.rs @@ -4,11 +4,14 @@ use crate::text::main::is_python_alpha::is_python_alpha; use crate::text::main::is_python_decimal::is_python_decimal; use crate::text::main::is_python_space::is_python_space; use crate::text::main::is_python_word::is_python_word; +use crate::text::main::python_casefold::python_casefold; use crate::text::main::python_cleandoc::python_cleandoc; use crate::text::main::python_text::python_text; +use crate::text::main::python_upper::python_upper; use crate::text::models::PythonText; use crate::text::tests::test_types::{ - CharacterClassTestCase, CleandocTestCase, CloseMatchesTestCase, PythonTextTestCase, + CaseMappingTestCase, CharacterClassTestCase, CleandocTestCase, CloseMatchesTestCase, + PythonTextTestCase, }; const KEYS: &[&str] = &[ @@ -285,3 +288,47 @@ fn given_python_versions_when_cleaning_bodies_then_cleandoc_matches_that_version ); } } + +#[test] +fn given_unicode_versions_when_folding_and_upper_casing_then_python_str_methods_agree() { + let test_cases = [ + CaseMappingTestCase { + description: "ASCII, final sigma, sharp s, dotted I and a ligature", + unicode_version: "15.0.0", + text: "Orders \u{3a3}\u{391}\u{3a3} \u{df} \u{130} \u{fb01}", + expected_casefold: "orders \u{3c3}\u{3b1}\u{3c3} ss i\u{307} fi", + expected_upper: "ORDERS \u{3a3}\u{391}\u{3a3} SS \u{130} FI", + }, + CaseMappingTestCase { + description: "a capital Unicode 16.0 added folds only there", + unicode_version: "15.1.0", + text: "\u{a7cb}", + expected_casefold: "\u{a7cb}", + expected_upper: "\u{a7cb}", + }, + CaseMappingTestCase { + description: "the same capital under Unicode 16.0", + unicode_version: "16.0.0", + text: "\u{a7cb}", + expected_casefold: "\u{264}", + expected_upper: "\u{a7cb}", + }, + ]; + for test_case in test_cases { + let python: PythonText = + python_text((3, 13), test_case.unicode_version).expect("the version is supported"); + + assert_eq!( + ( + python_casefold(python, test_case.text), + python_upper(python, test_case.text) + ), + ( + test_case.expected_casefold.to_owned(), + test_case.expected_upper.to_owned() + ), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-core/src/text/tests/test_types.rs b/crates/sqlbuild-core/src/text/tests/test_types.rs index 4b5d761cf5..b5d49dfec2 100644 --- a/crates/sqlbuild-core/src/text/tests/test_types.rs +++ b/crates/sqlbuild-core/src/text/tests/test_types.rs @@ -52,3 +52,11 @@ pub(super) struct CleandocTestCase { pub(super) text: &'static str, pub(super) expected_text: &'static str, } + +pub(super) struct CaseMappingTestCase { + pub(super) description: &'static str, + pub(super) unicode_version: &'static str, + pub(super) text: &'static str, + pub(super) expected_casefold: &'static str, + pub(super) expected_upper: &'static str, +} diff --git a/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_0_0.in b/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_0_0.in new file mode 100644 index 0000000000..a45b06a971 --- /dev/null +++ b/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_0_0.in @@ -0,0 +1,1529 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.upper()` changes in Unicode 15.0.0, in code point order. +&[ + (0x0061, "\u{41}"), + (0x0062, "\u{42}"), + (0x0063, "\u{43}"), + (0x0064, "\u{44}"), + (0x0065, "\u{45}"), + (0x0066, "\u{46}"), + (0x0067, "\u{47}"), + (0x0068, "\u{48}"), + (0x0069, "\u{49}"), + (0x006A, "\u{4a}"), + (0x006B, "\u{4b}"), + (0x006C, "\u{4c}"), + (0x006D, "\u{4d}"), + (0x006E, "\u{4e}"), + (0x006F, "\u{4f}"), + (0x0070, "\u{50}"), + (0x0071, "\u{51}"), + (0x0072, "\u{52}"), + (0x0073, "\u{53}"), + (0x0074, "\u{54}"), + (0x0075, "\u{55}"), + (0x0076, "\u{56}"), + (0x0077, "\u{57}"), + (0x0078, "\u{58}"), + (0x0079, "\u{59}"), + (0x007A, "\u{5a}"), + (0x00B5, "\u{39c}"), + (0x00DF, "\u{53}\u{53}"), + (0x00E0, "\u{c0}"), + (0x00E1, "\u{c1}"), + (0x00E2, "\u{c2}"), + (0x00E3, "\u{c3}"), + (0x00E4, "\u{c4}"), + (0x00E5, "\u{c5}"), + (0x00E6, "\u{c6}"), + (0x00E7, "\u{c7}"), + (0x00E8, "\u{c8}"), + (0x00E9, "\u{c9}"), + (0x00EA, "\u{ca}"), + (0x00EB, "\u{cb}"), + (0x00EC, "\u{cc}"), + (0x00ED, "\u{cd}"), + (0x00EE, "\u{ce}"), + (0x00EF, "\u{cf}"), + (0x00F0, "\u{d0}"), + (0x00F1, "\u{d1}"), + (0x00F2, "\u{d2}"), + (0x00F3, "\u{d3}"), + (0x00F4, "\u{d4}"), + (0x00F5, "\u{d5}"), + (0x00F6, "\u{d6}"), + (0x00F8, "\u{d8}"), + (0x00F9, "\u{d9}"), + (0x00FA, "\u{da}"), + (0x00FB, "\u{db}"), + (0x00FC, "\u{dc}"), + (0x00FD, "\u{dd}"), + (0x00FE, "\u{de}"), + (0x00FF, "\u{178}"), + (0x0101, "\u{100}"), + (0x0103, "\u{102}"), + (0x0105, "\u{104}"), + (0x0107, "\u{106}"), + (0x0109, "\u{108}"), + (0x010B, "\u{10a}"), + (0x010D, "\u{10c}"), + (0x010F, "\u{10e}"), + (0x0111, "\u{110}"), + (0x0113, "\u{112}"), + (0x0115, "\u{114}"), + (0x0117, "\u{116}"), + (0x0119, "\u{118}"), + (0x011B, "\u{11a}"), + (0x011D, "\u{11c}"), + (0x011F, "\u{11e}"), + (0x0121, "\u{120}"), + (0x0123, "\u{122}"), + (0x0125, "\u{124}"), + (0x0127, "\u{126}"), + (0x0129, "\u{128}"), + (0x012B, "\u{12a}"), + (0x012D, "\u{12c}"), + (0x012F, "\u{12e}"), + (0x0131, "\u{49}"), + (0x0133, "\u{132}"), + (0x0135, "\u{134}"), + (0x0137, "\u{136}"), + (0x013A, "\u{139}"), + (0x013C, "\u{13b}"), + (0x013E, "\u{13d}"), + (0x0140, "\u{13f}"), + (0x0142, "\u{141}"), + (0x0144, "\u{143}"), + (0x0146, "\u{145}"), + (0x0148, "\u{147}"), + (0x0149, "\u{2bc}\u{4e}"), + (0x014B, "\u{14a}"), + (0x014D, "\u{14c}"), + (0x014F, "\u{14e}"), + (0x0151, "\u{150}"), + (0x0153, "\u{152}"), + (0x0155, "\u{154}"), + (0x0157, "\u{156}"), + (0x0159, "\u{158}"), + (0x015B, "\u{15a}"), + (0x015D, "\u{15c}"), + (0x015F, "\u{15e}"), + (0x0161, "\u{160}"), + (0x0163, "\u{162}"), + (0x0165, "\u{164}"), + (0x0167, "\u{166}"), + (0x0169, "\u{168}"), + (0x016B, "\u{16a}"), + (0x016D, "\u{16c}"), + (0x016F, "\u{16e}"), + (0x0171, "\u{170}"), + (0x0173, "\u{172}"), + (0x0175, "\u{174}"), + (0x0177, "\u{176}"), + (0x017A, "\u{179}"), + (0x017C, "\u{17b}"), + (0x017E, "\u{17d}"), + (0x017F, "\u{53}"), + (0x0180, "\u{243}"), + (0x0183, "\u{182}"), + (0x0185, "\u{184}"), + (0x0188, "\u{187}"), + (0x018C, "\u{18b}"), + (0x0192, "\u{191}"), + (0x0195, "\u{1f6}"), + (0x0199, "\u{198}"), + (0x019A, "\u{23d}"), + (0x019E, "\u{220}"), + (0x01A1, "\u{1a0}"), + (0x01A3, "\u{1a2}"), + (0x01A5, "\u{1a4}"), + (0x01A8, "\u{1a7}"), + (0x01AD, "\u{1ac}"), + (0x01B0, "\u{1af}"), + (0x01B4, "\u{1b3}"), + (0x01B6, "\u{1b5}"), + (0x01B9, "\u{1b8}"), + (0x01BD, "\u{1bc}"), + (0x01BF, "\u{1f7}"), + (0x01C5, "\u{1c4}"), + (0x01C6, "\u{1c4}"), + (0x01C8, "\u{1c7}"), + (0x01C9, "\u{1c7}"), + (0x01CB, "\u{1ca}"), + (0x01CC, "\u{1ca}"), + (0x01CE, "\u{1cd}"), + (0x01D0, "\u{1cf}"), + (0x01D2, "\u{1d1}"), + (0x01D4, "\u{1d3}"), + (0x01D6, "\u{1d5}"), + (0x01D8, "\u{1d7}"), + (0x01DA, "\u{1d9}"), + (0x01DC, "\u{1db}"), + (0x01DD, "\u{18e}"), + (0x01DF, "\u{1de}"), + (0x01E1, "\u{1e0}"), + (0x01E3, "\u{1e2}"), + (0x01E5, "\u{1e4}"), + (0x01E7, "\u{1e6}"), + (0x01E9, "\u{1e8}"), + (0x01EB, "\u{1ea}"), + (0x01ED, "\u{1ec}"), + (0x01EF, "\u{1ee}"), + (0x01F0, "\u{4a}\u{30c}"), + (0x01F2, "\u{1f1}"), + (0x01F3, "\u{1f1}"), + (0x01F5, "\u{1f4}"), + (0x01F9, "\u{1f8}"), + (0x01FB, "\u{1fa}"), + (0x01FD, "\u{1fc}"), + (0x01FF, "\u{1fe}"), + (0x0201, "\u{200}"), + (0x0203, "\u{202}"), + (0x0205, "\u{204}"), + (0x0207, "\u{206}"), + (0x0209, "\u{208}"), + (0x020B, "\u{20a}"), + (0x020D, "\u{20c}"), + (0x020F, "\u{20e}"), + (0x0211, "\u{210}"), + (0x0213, "\u{212}"), + (0x0215, "\u{214}"), + (0x0217, "\u{216}"), + (0x0219, "\u{218}"), + (0x021B, "\u{21a}"), + (0x021D, "\u{21c}"), + (0x021F, "\u{21e}"), + (0x0223, "\u{222}"), + (0x0225, "\u{224}"), + (0x0227, "\u{226}"), + (0x0229, "\u{228}"), + (0x022B, "\u{22a}"), + (0x022D, "\u{22c}"), + (0x022F, "\u{22e}"), + (0x0231, "\u{230}"), + (0x0233, "\u{232}"), + (0x023C, "\u{23b}"), + (0x023F, "\u{2c7e}"), + (0x0240, "\u{2c7f}"), + (0x0242, "\u{241}"), + (0x0247, "\u{246}"), + (0x0249, "\u{248}"), + (0x024B, "\u{24a}"), + (0x024D, "\u{24c}"), + (0x024F, "\u{24e}"), + (0x0250, "\u{2c6f}"), + (0x0251, "\u{2c6d}"), + (0x0252, "\u{2c70}"), + (0x0253, "\u{181}"), + (0x0254, "\u{186}"), + (0x0256, "\u{189}"), + (0x0257, "\u{18a}"), + (0x0259, "\u{18f}"), + (0x025B, "\u{190}"), + (0x025C, "\u{a7ab}"), + (0x0260, "\u{193}"), + (0x0261, "\u{a7ac}"), + (0x0263, "\u{194}"), + (0x0265, "\u{a78d}"), + (0x0266, "\u{a7aa}"), + (0x0268, "\u{197}"), + (0x0269, "\u{196}"), + (0x026A, "\u{a7ae}"), + (0x026B, "\u{2c62}"), + (0x026C, "\u{a7ad}"), + (0x026F, "\u{19c}"), + (0x0271, "\u{2c6e}"), + (0x0272, "\u{19d}"), + (0x0275, "\u{19f}"), + (0x027D, "\u{2c64}"), + (0x0280, "\u{1a6}"), + (0x0282, "\u{a7c5}"), + (0x0283, "\u{1a9}"), + (0x0287, "\u{a7b1}"), + (0x0288, "\u{1ae}"), + (0x0289, "\u{244}"), + (0x028A, "\u{1b1}"), + (0x028B, "\u{1b2}"), + (0x028C, "\u{245}"), + (0x0292, "\u{1b7}"), + (0x029D, "\u{a7b2}"), + (0x029E, "\u{a7b0}"), + (0x0345, "\u{399}"), + (0x0371, "\u{370}"), + (0x0373, "\u{372}"), + (0x0377, "\u{376}"), + (0x037B, "\u{3fd}"), + (0x037C, "\u{3fe}"), + (0x037D, "\u{3ff}"), + (0x0390, "\u{399}\u{308}\u{301}"), + (0x03AC, "\u{386}"), + (0x03AD, "\u{388}"), + (0x03AE, "\u{389}"), + (0x03AF, "\u{38a}"), + (0x03B0, "\u{3a5}\u{308}\u{301}"), + (0x03B1, "\u{391}"), + (0x03B2, "\u{392}"), + (0x03B3, "\u{393}"), + (0x03B4, "\u{394}"), + (0x03B5, "\u{395}"), + (0x03B6, "\u{396}"), + (0x03B7, "\u{397}"), + (0x03B8, "\u{398}"), + (0x03B9, "\u{399}"), + (0x03BA, "\u{39a}"), + (0x03BB, "\u{39b}"), + (0x03BC, "\u{39c}"), + (0x03BD, "\u{39d}"), + (0x03BE, "\u{39e}"), + (0x03BF, "\u{39f}"), + (0x03C0, "\u{3a0}"), + (0x03C1, "\u{3a1}"), + (0x03C2, "\u{3a3}"), + (0x03C3, "\u{3a3}"), + (0x03C4, "\u{3a4}"), + (0x03C5, "\u{3a5}"), + (0x03C6, "\u{3a6}"), + (0x03C7, "\u{3a7}"), + (0x03C8, "\u{3a8}"), + (0x03C9, "\u{3a9}"), + (0x03CA, "\u{3aa}"), + (0x03CB, "\u{3ab}"), + (0x03CC, "\u{38c}"), + (0x03CD, "\u{38e}"), + (0x03CE, "\u{38f}"), + (0x03D0, "\u{392}"), + (0x03D1, "\u{398}"), + (0x03D5, "\u{3a6}"), + (0x03D6, "\u{3a0}"), + (0x03D7, "\u{3cf}"), + (0x03D9, "\u{3d8}"), + (0x03DB, "\u{3da}"), + (0x03DD, "\u{3dc}"), + (0x03DF, "\u{3de}"), + (0x03E1, "\u{3e0}"), + (0x03E3, "\u{3e2}"), + (0x03E5, "\u{3e4}"), + (0x03E7, "\u{3e6}"), + (0x03E9, "\u{3e8}"), + (0x03EB, "\u{3ea}"), + (0x03ED, "\u{3ec}"), + (0x03EF, "\u{3ee}"), + (0x03F0, "\u{39a}"), + (0x03F1, "\u{3a1}"), + (0x03F2, "\u{3f9}"), + (0x03F3, "\u{37f}"), + (0x03F5, "\u{395}"), + (0x03F8, "\u{3f7}"), + (0x03FB, "\u{3fa}"), + (0x0430, "\u{410}"), + (0x0431, "\u{411}"), + (0x0432, "\u{412}"), + (0x0433, "\u{413}"), + (0x0434, "\u{414}"), + (0x0435, "\u{415}"), + (0x0436, "\u{416}"), + (0x0437, "\u{417}"), + (0x0438, "\u{418}"), + (0x0439, "\u{419}"), + (0x043A, "\u{41a}"), + (0x043B, "\u{41b}"), + (0x043C, "\u{41c}"), + (0x043D, "\u{41d}"), + (0x043E, "\u{41e}"), + (0x043F, "\u{41f}"), + (0x0440, "\u{420}"), + (0x0441, "\u{421}"), + (0x0442, "\u{422}"), + (0x0443, "\u{423}"), + (0x0444, "\u{424}"), + (0x0445, "\u{425}"), + (0x0446, "\u{426}"), + (0x0447, "\u{427}"), + (0x0448, "\u{428}"), + (0x0449, "\u{429}"), + (0x044A, "\u{42a}"), + (0x044B, "\u{42b}"), + (0x044C, "\u{42c}"), + (0x044D, "\u{42d}"), + (0x044E, "\u{42e}"), + (0x044F, "\u{42f}"), + (0x0450, "\u{400}"), + (0x0451, "\u{401}"), + (0x0452, "\u{402}"), + (0x0453, "\u{403}"), + (0x0454, "\u{404}"), + (0x0455, "\u{405}"), + (0x0456, "\u{406}"), + (0x0457, "\u{407}"), + (0x0458, "\u{408}"), + (0x0459, "\u{409}"), + (0x045A, "\u{40a}"), + (0x045B, "\u{40b}"), + (0x045C, "\u{40c}"), + (0x045D, "\u{40d}"), + (0x045E, "\u{40e}"), + (0x045F, "\u{40f}"), + (0x0461, "\u{460}"), + (0x0463, "\u{462}"), + (0x0465, "\u{464}"), + (0x0467, "\u{466}"), + (0x0469, "\u{468}"), + (0x046B, "\u{46a}"), + (0x046D, "\u{46c}"), + (0x046F, "\u{46e}"), + (0x0471, "\u{470}"), + (0x0473, "\u{472}"), + (0x0475, "\u{474}"), + (0x0477, "\u{476}"), + (0x0479, "\u{478}"), + (0x047B, "\u{47a}"), + (0x047D, "\u{47c}"), + (0x047F, "\u{47e}"), + (0x0481, "\u{480}"), + (0x048B, "\u{48a}"), + (0x048D, "\u{48c}"), + (0x048F, "\u{48e}"), + (0x0491, "\u{490}"), + (0x0493, "\u{492}"), + (0x0495, "\u{494}"), + (0x0497, "\u{496}"), + (0x0499, "\u{498}"), + (0x049B, "\u{49a}"), + (0x049D, "\u{49c}"), + (0x049F, "\u{49e}"), + (0x04A1, "\u{4a0}"), + (0x04A3, "\u{4a2}"), + (0x04A5, "\u{4a4}"), + (0x04A7, "\u{4a6}"), + (0x04A9, "\u{4a8}"), + (0x04AB, "\u{4aa}"), + (0x04AD, "\u{4ac}"), + (0x04AF, "\u{4ae}"), + (0x04B1, "\u{4b0}"), + (0x04B3, "\u{4b2}"), + (0x04B5, "\u{4b4}"), + (0x04B7, "\u{4b6}"), + (0x04B9, "\u{4b8}"), + (0x04BB, "\u{4ba}"), + (0x04BD, "\u{4bc}"), + (0x04BF, "\u{4be}"), + (0x04C2, "\u{4c1}"), + (0x04C4, "\u{4c3}"), + (0x04C6, "\u{4c5}"), + (0x04C8, "\u{4c7}"), + (0x04CA, "\u{4c9}"), + (0x04CC, "\u{4cb}"), + (0x04CE, "\u{4cd}"), + (0x04CF, "\u{4c0}"), + (0x04D1, "\u{4d0}"), + (0x04D3, "\u{4d2}"), + (0x04D5, "\u{4d4}"), + (0x04D7, "\u{4d6}"), + (0x04D9, "\u{4d8}"), + (0x04DB, "\u{4da}"), + (0x04DD, "\u{4dc}"), + (0x04DF, "\u{4de}"), + (0x04E1, "\u{4e0}"), + (0x04E3, "\u{4e2}"), + (0x04E5, "\u{4e4}"), + (0x04E7, "\u{4e6}"), + (0x04E9, "\u{4e8}"), + (0x04EB, "\u{4ea}"), + (0x04ED, "\u{4ec}"), + (0x04EF, "\u{4ee}"), + (0x04F1, "\u{4f0}"), + (0x04F3, "\u{4f2}"), + (0x04F5, "\u{4f4}"), + (0x04F7, "\u{4f6}"), + (0x04F9, "\u{4f8}"), + (0x04FB, "\u{4fa}"), + (0x04FD, "\u{4fc}"), + (0x04FF, "\u{4fe}"), + (0x0501, "\u{500}"), + (0x0503, "\u{502}"), + (0x0505, "\u{504}"), + (0x0507, "\u{506}"), + (0x0509, "\u{508}"), + (0x050B, "\u{50a}"), + (0x050D, "\u{50c}"), + (0x050F, "\u{50e}"), + (0x0511, "\u{510}"), + (0x0513, "\u{512}"), + (0x0515, "\u{514}"), + (0x0517, "\u{516}"), + (0x0519, "\u{518}"), + (0x051B, "\u{51a}"), + (0x051D, "\u{51c}"), + (0x051F, "\u{51e}"), + (0x0521, "\u{520}"), + (0x0523, "\u{522}"), + (0x0525, "\u{524}"), + (0x0527, "\u{526}"), + (0x0529, "\u{528}"), + (0x052B, "\u{52a}"), + (0x052D, "\u{52c}"), + (0x052F, "\u{52e}"), + (0x0561, "\u{531}"), + (0x0562, "\u{532}"), + (0x0563, "\u{533}"), + (0x0564, "\u{534}"), + (0x0565, "\u{535}"), + (0x0566, "\u{536}"), + (0x0567, "\u{537}"), + (0x0568, "\u{538}"), + (0x0569, "\u{539}"), + (0x056A, "\u{53a}"), + (0x056B, "\u{53b}"), + (0x056C, "\u{53c}"), + (0x056D, "\u{53d}"), + (0x056E, "\u{53e}"), + (0x056F, "\u{53f}"), + (0x0570, "\u{540}"), + (0x0571, "\u{541}"), + (0x0572, "\u{542}"), + (0x0573, "\u{543}"), + (0x0574, "\u{544}"), + (0x0575, "\u{545}"), + (0x0576, "\u{546}"), + (0x0577, "\u{547}"), + (0x0578, "\u{548}"), + (0x0579, "\u{549}"), + (0x057A, "\u{54a}"), + (0x057B, "\u{54b}"), + (0x057C, "\u{54c}"), + (0x057D, "\u{54d}"), + (0x057E, "\u{54e}"), + (0x057F, "\u{54f}"), + (0x0580, "\u{550}"), + (0x0581, "\u{551}"), + (0x0582, "\u{552}"), + (0x0583, "\u{553}"), + (0x0584, "\u{554}"), + (0x0585, "\u{555}"), + (0x0586, "\u{556}"), + (0x0587, "\u{535}\u{552}"), + (0x10D0, "\u{1c90}"), + (0x10D1, "\u{1c91}"), + (0x10D2, "\u{1c92}"), + (0x10D3, "\u{1c93}"), + (0x10D4, "\u{1c94}"), + (0x10D5, "\u{1c95}"), + (0x10D6, "\u{1c96}"), + (0x10D7, "\u{1c97}"), + (0x10D8, "\u{1c98}"), + (0x10D9, "\u{1c99}"), + (0x10DA, "\u{1c9a}"), + (0x10DB, "\u{1c9b}"), + (0x10DC, "\u{1c9c}"), + (0x10DD, "\u{1c9d}"), + (0x10DE, "\u{1c9e}"), + (0x10DF, "\u{1c9f}"), + (0x10E0, "\u{1ca0}"), + (0x10E1, "\u{1ca1}"), + (0x10E2, "\u{1ca2}"), + (0x10E3, "\u{1ca3}"), + (0x10E4, "\u{1ca4}"), + (0x10E5, "\u{1ca5}"), + (0x10E6, "\u{1ca6}"), + (0x10E7, "\u{1ca7}"), + (0x10E8, "\u{1ca8}"), + (0x10E9, "\u{1ca9}"), + (0x10EA, "\u{1caa}"), + (0x10EB, "\u{1cab}"), + (0x10EC, "\u{1cac}"), + (0x10ED, "\u{1cad}"), + (0x10EE, "\u{1cae}"), + (0x10EF, "\u{1caf}"), + (0x10F0, "\u{1cb0}"), + (0x10F1, "\u{1cb1}"), + (0x10F2, "\u{1cb2}"), + (0x10F3, "\u{1cb3}"), + (0x10F4, "\u{1cb4}"), + (0x10F5, "\u{1cb5}"), + (0x10F6, "\u{1cb6}"), + (0x10F7, "\u{1cb7}"), + (0x10F8, "\u{1cb8}"), + (0x10F9, "\u{1cb9}"), + (0x10FA, "\u{1cba}"), + (0x10FD, "\u{1cbd}"), + (0x10FE, "\u{1cbe}"), + (0x10FF, "\u{1cbf}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{412}"), + (0x1C81, "\u{414}"), + (0x1C82, "\u{41e}"), + (0x1C83, "\u{421}"), + (0x1C84, "\u{422}"), + (0x1C85, "\u{422}"), + (0x1C86, "\u{42a}"), + (0x1C87, "\u{462}"), + (0x1C88, "\u{a64a}"), + (0x1D79, "\u{a77d}"), + (0x1D7D, "\u{2c63}"), + (0x1D8E, "\u{a7c6}"), + (0x1E01, "\u{1e00}"), + (0x1E03, "\u{1e02}"), + (0x1E05, "\u{1e04}"), + (0x1E07, "\u{1e06}"), + (0x1E09, "\u{1e08}"), + (0x1E0B, "\u{1e0a}"), + (0x1E0D, "\u{1e0c}"), + (0x1E0F, "\u{1e0e}"), + (0x1E11, "\u{1e10}"), + (0x1E13, "\u{1e12}"), + (0x1E15, "\u{1e14}"), + (0x1E17, "\u{1e16}"), + (0x1E19, "\u{1e18}"), + (0x1E1B, "\u{1e1a}"), + (0x1E1D, "\u{1e1c}"), + (0x1E1F, "\u{1e1e}"), + (0x1E21, "\u{1e20}"), + (0x1E23, "\u{1e22}"), + (0x1E25, "\u{1e24}"), + (0x1E27, "\u{1e26}"), + (0x1E29, "\u{1e28}"), + (0x1E2B, "\u{1e2a}"), + (0x1E2D, "\u{1e2c}"), + (0x1E2F, "\u{1e2e}"), + (0x1E31, "\u{1e30}"), + (0x1E33, "\u{1e32}"), + (0x1E35, "\u{1e34}"), + (0x1E37, "\u{1e36}"), + (0x1E39, "\u{1e38}"), + (0x1E3B, "\u{1e3a}"), + (0x1E3D, "\u{1e3c}"), + (0x1E3F, "\u{1e3e}"), + (0x1E41, "\u{1e40}"), + (0x1E43, "\u{1e42}"), + (0x1E45, "\u{1e44}"), + (0x1E47, "\u{1e46}"), + (0x1E49, "\u{1e48}"), + (0x1E4B, "\u{1e4a}"), + (0x1E4D, "\u{1e4c}"), + (0x1E4F, "\u{1e4e}"), + (0x1E51, "\u{1e50}"), + (0x1E53, "\u{1e52}"), + (0x1E55, "\u{1e54}"), + (0x1E57, "\u{1e56}"), + (0x1E59, "\u{1e58}"), + (0x1E5B, "\u{1e5a}"), + (0x1E5D, "\u{1e5c}"), + (0x1E5F, "\u{1e5e}"), + (0x1E61, "\u{1e60}"), + (0x1E63, "\u{1e62}"), + (0x1E65, "\u{1e64}"), + (0x1E67, "\u{1e66}"), + (0x1E69, "\u{1e68}"), + (0x1E6B, "\u{1e6a}"), + (0x1E6D, "\u{1e6c}"), + (0x1E6F, "\u{1e6e}"), + (0x1E71, "\u{1e70}"), + (0x1E73, "\u{1e72}"), + (0x1E75, "\u{1e74}"), + (0x1E77, "\u{1e76}"), + (0x1E79, "\u{1e78}"), + (0x1E7B, "\u{1e7a}"), + (0x1E7D, "\u{1e7c}"), + (0x1E7F, "\u{1e7e}"), + (0x1E81, "\u{1e80}"), + (0x1E83, "\u{1e82}"), + (0x1E85, "\u{1e84}"), + (0x1E87, "\u{1e86}"), + (0x1E89, "\u{1e88}"), + (0x1E8B, "\u{1e8a}"), + (0x1E8D, "\u{1e8c}"), + (0x1E8F, "\u{1e8e}"), + (0x1E91, "\u{1e90}"), + (0x1E93, "\u{1e92}"), + (0x1E95, "\u{1e94}"), + (0x1E96, "\u{48}\u{331}"), + (0x1E97, "\u{54}\u{308}"), + (0x1E98, "\u{57}\u{30a}"), + (0x1E99, "\u{59}\u{30a}"), + (0x1E9A, "\u{41}\u{2be}"), + (0x1E9B, "\u{1e60}"), + (0x1EA1, "\u{1ea0}"), + (0x1EA3, "\u{1ea2}"), + (0x1EA5, "\u{1ea4}"), + (0x1EA7, "\u{1ea6}"), + (0x1EA9, "\u{1ea8}"), + (0x1EAB, "\u{1eaa}"), + (0x1EAD, "\u{1eac}"), + (0x1EAF, "\u{1eae}"), + (0x1EB1, "\u{1eb0}"), + (0x1EB3, "\u{1eb2}"), + (0x1EB5, "\u{1eb4}"), + (0x1EB7, "\u{1eb6}"), + (0x1EB9, "\u{1eb8}"), + (0x1EBB, "\u{1eba}"), + (0x1EBD, "\u{1ebc}"), + (0x1EBF, "\u{1ebe}"), + (0x1EC1, "\u{1ec0}"), + (0x1EC3, "\u{1ec2}"), + (0x1EC5, "\u{1ec4}"), + (0x1EC7, "\u{1ec6}"), + (0x1EC9, "\u{1ec8}"), + (0x1ECB, "\u{1eca}"), + (0x1ECD, "\u{1ecc}"), + (0x1ECF, "\u{1ece}"), + (0x1ED1, "\u{1ed0}"), + (0x1ED3, "\u{1ed2}"), + (0x1ED5, "\u{1ed4}"), + (0x1ED7, "\u{1ed6}"), + (0x1ED9, "\u{1ed8}"), + (0x1EDB, "\u{1eda}"), + (0x1EDD, "\u{1edc}"), + (0x1EDF, "\u{1ede}"), + (0x1EE1, "\u{1ee0}"), + (0x1EE3, "\u{1ee2}"), + (0x1EE5, "\u{1ee4}"), + (0x1EE7, "\u{1ee6}"), + (0x1EE9, "\u{1ee8}"), + (0x1EEB, "\u{1eea}"), + (0x1EED, "\u{1eec}"), + (0x1EEF, "\u{1eee}"), + (0x1EF1, "\u{1ef0}"), + (0x1EF3, "\u{1ef2}"), + (0x1EF5, "\u{1ef4}"), + (0x1EF7, "\u{1ef6}"), + (0x1EF9, "\u{1ef8}"), + (0x1EFB, "\u{1efa}"), + (0x1EFD, "\u{1efc}"), + (0x1EFF, "\u{1efe}"), + (0x1F00, "\u{1f08}"), + (0x1F01, "\u{1f09}"), + (0x1F02, "\u{1f0a}"), + (0x1F03, "\u{1f0b}"), + (0x1F04, "\u{1f0c}"), + (0x1F05, "\u{1f0d}"), + (0x1F06, "\u{1f0e}"), + (0x1F07, "\u{1f0f}"), + (0x1F10, "\u{1f18}"), + (0x1F11, "\u{1f19}"), + (0x1F12, "\u{1f1a}"), + (0x1F13, "\u{1f1b}"), + (0x1F14, "\u{1f1c}"), + (0x1F15, "\u{1f1d}"), + (0x1F20, "\u{1f28}"), + (0x1F21, "\u{1f29}"), + (0x1F22, "\u{1f2a}"), + (0x1F23, "\u{1f2b}"), + (0x1F24, "\u{1f2c}"), + (0x1F25, "\u{1f2d}"), + (0x1F26, "\u{1f2e}"), + (0x1F27, "\u{1f2f}"), + (0x1F30, "\u{1f38}"), + (0x1F31, "\u{1f39}"), + (0x1F32, "\u{1f3a}"), + (0x1F33, "\u{1f3b}"), + (0x1F34, "\u{1f3c}"), + (0x1F35, "\u{1f3d}"), + (0x1F36, "\u{1f3e}"), + (0x1F37, "\u{1f3f}"), + (0x1F40, "\u{1f48}"), + (0x1F41, "\u{1f49}"), + (0x1F42, "\u{1f4a}"), + (0x1F43, "\u{1f4b}"), + (0x1F44, "\u{1f4c}"), + (0x1F45, "\u{1f4d}"), + (0x1F50, "\u{3a5}\u{313}"), + (0x1F51, "\u{1f59}"), + (0x1F52, "\u{3a5}\u{313}\u{300}"), + (0x1F53, "\u{1f5b}"), + (0x1F54, "\u{3a5}\u{313}\u{301}"), + (0x1F55, "\u{1f5d}"), + (0x1F56, "\u{3a5}\u{313}\u{342}"), + (0x1F57, "\u{1f5f}"), + (0x1F60, "\u{1f68}"), + (0x1F61, "\u{1f69}"), + (0x1F62, "\u{1f6a}"), + (0x1F63, "\u{1f6b}"), + (0x1F64, "\u{1f6c}"), + (0x1F65, "\u{1f6d}"), + (0x1F66, "\u{1f6e}"), + (0x1F67, "\u{1f6f}"), + (0x1F70, "\u{1fba}"), + (0x1F71, "\u{1fbb}"), + (0x1F72, "\u{1fc8}"), + (0x1F73, "\u{1fc9}"), + (0x1F74, "\u{1fca}"), + (0x1F75, "\u{1fcb}"), + (0x1F76, "\u{1fda}"), + (0x1F77, "\u{1fdb}"), + (0x1F78, "\u{1ff8}"), + (0x1F79, "\u{1ff9}"), + (0x1F7A, "\u{1fea}"), + (0x1F7B, "\u{1feb}"), + (0x1F7C, "\u{1ffa}"), + (0x1F7D, "\u{1ffb}"), + (0x1F80, "\u{1f08}\u{399}"), + (0x1F81, "\u{1f09}\u{399}"), + (0x1F82, "\u{1f0a}\u{399}"), + (0x1F83, "\u{1f0b}\u{399}"), + (0x1F84, "\u{1f0c}\u{399}"), + (0x1F85, "\u{1f0d}\u{399}"), + (0x1F86, "\u{1f0e}\u{399}"), + (0x1F87, "\u{1f0f}\u{399}"), + (0x1F88, "\u{1f08}\u{399}"), + (0x1F89, "\u{1f09}\u{399}"), + (0x1F8A, "\u{1f0a}\u{399}"), + (0x1F8B, "\u{1f0b}\u{399}"), + (0x1F8C, "\u{1f0c}\u{399}"), + (0x1F8D, "\u{1f0d}\u{399}"), + (0x1F8E, "\u{1f0e}\u{399}"), + (0x1F8F, "\u{1f0f}\u{399}"), + (0x1F90, "\u{1f28}\u{399}"), + (0x1F91, "\u{1f29}\u{399}"), + (0x1F92, "\u{1f2a}\u{399}"), + (0x1F93, "\u{1f2b}\u{399}"), + (0x1F94, "\u{1f2c}\u{399}"), + (0x1F95, "\u{1f2d}\u{399}"), + (0x1F96, "\u{1f2e}\u{399}"), + (0x1F97, "\u{1f2f}\u{399}"), + (0x1F98, "\u{1f28}\u{399}"), + (0x1F99, "\u{1f29}\u{399}"), + (0x1F9A, "\u{1f2a}\u{399}"), + (0x1F9B, "\u{1f2b}\u{399}"), + (0x1F9C, "\u{1f2c}\u{399}"), + (0x1F9D, "\u{1f2d}\u{399}"), + (0x1F9E, "\u{1f2e}\u{399}"), + (0x1F9F, "\u{1f2f}\u{399}"), + (0x1FA0, "\u{1f68}\u{399}"), + (0x1FA1, "\u{1f69}\u{399}"), + (0x1FA2, "\u{1f6a}\u{399}"), + (0x1FA3, "\u{1f6b}\u{399}"), + (0x1FA4, "\u{1f6c}\u{399}"), + (0x1FA5, "\u{1f6d}\u{399}"), + (0x1FA6, "\u{1f6e}\u{399}"), + (0x1FA7, "\u{1f6f}\u{399}"), + (0x1FA8, "\u{1f68}\u{399}"), + (0x1FA9, "\u{1f69}\u{399}"), + (0x1FAA, "\u{1f6a}\u{399}"), + (0x1FAB, "\u{1f6b}\u{399}"), + (0x1FAC, "\u{1f6c}\u{399}"), + (0x1FAD, "\u{1f6d}\u{399}"), + (0x1FAE, "\u{1f6e}\u{399}"), + (0x1FAF, "\u{1f6f}\u{399}"), + (0x1FB0, "\u{1fb8}"), + (0x1FB1, "\u{1fb9}"), + (0x1FB2, "\u{1fba}\u{399}"), + (0x1FB3, "\u{391}\u{399}"), + (0x1FB4, "\u{386}\u{399}"), + (0x1FB6, "\u{391}\u{342}"), + (0x1FB7, "\u{391}\u{342}\u{399}"), + (0x1FBC, "\u{391}\u{399}"), + (0x1FBE, "\u{399}"), + (0x1FC2, "\u{1fca}\u{399}"), + (0x1FC3, "\u{397}\u{399}"), + (0x1FC4, "\u{389}\u{399}"), + (0x1FC6, "\u{397}\u{342}"), + (0x1FC7, "\u{397}\u{342}\u{399}"), + (0x1FCC, "\u{397}\u{399}"), + (0x1FD0, "\u{1fd8}"), + (0x1FD1, "\u{1fd9}"), + (0x1FD2, "\u{399}\u{308}\u{300}"), + (0x1FD3, "\u{399}\u{308}\u{301}"), + (0x1FD6, "\u{399}\u{342}"), + (0x1FD7, "\u{399}\u{308}\u{342}"), + (0x1FE0, "\u{1fe8}"), + (0x1FE1, "\u{1fe9}"), + (0x1FE2, "\u{3a5}\u{308}\u{300}"), + (0x1FE3, "\u{3a5}\u{308}\u{301}"), + (0x1FE4, "\u{3a1}\u{313}"), + (0x1FE5, "\u{1fec}"), + (0x1FE6, "\u{3a5}\u{342}"), + (0x1FE7, "\u{3a5}\u{308}\u{342}"), + (0x1FF2, "\u{1ffa}\u{399}"), + (0x1FF3, "\u{3a9}\u{399}"), + (0x1FF4, "\u{38f}\u{399}"), + (0x1FF6, "\u{3a9}\u{342}"), + (0x1FF7, "\u{3a9}\u{342}\u{399}"), + (0x1FFC, "\u{3a9}\u{399}"), + (0x214E, "\u{2132}"), + (0x2170, "\u{2160}"), + (0x2171, "\u{2161}"), + (0x2172, "\u{2162}"), + (0x2173, "\u{2163}"), + (0x2174, "\u{2164}"), + (0x2175, "\u{2165}"), + (0x2176, "\u{2166}"), + (0x2177, "\u{2167}"), + (0x2178, "\u{2168}"), + (0x2179, "\u{2169}"), + (0x217A, "\u{216a}"), + (0x217B, "\u{216b}"), + (0x217C, "\u{216c}"), + (0x217D, "\u{216d}"), + (0x217E, "\u{216e}"), + (0x217F, "\u{216f}"), + (0x2184, "\u{2183}"), + (0x24D0, "\u{24b6}"), + (0x24D1, "\u{24b7}"), + (0x24D2, "\u{24b8}"), + (0x24D3, "\u{24b9}"), + (0x24D4, "\u{24ba}"), + (0x24D5, "\u{24bb}"), + (0x24D6, "\u{24bc}"), + (0x24D7, "\u{24bd}"), + (0x24D8, "\u{24be}"), + (0x24D9, "\u{24bf}"), + (0x24DA, "\u{24c0}"), + (0x24DB, "\u{24c1}"), + (0x24DC, "\u{24c2}"), + (0x24DD, "\u{24c3}"), + (0x24DE, "\u{24c4}"), + (0x24DF, "\u{24c5}"), + (0x24E0, "\u{24c6}"), + (0x24E1, "\u{24c7}"), + (0x24E2, "\u{24c8}"), + (0x24E3, "\u{24c9}"), + (0x24E4, "\u{24ca}"), + (0x24E5, "\u{24cb}"), + (0x24E6, "\u{24cc}"), + (0x24E7, "\u{24cd}"), + (0x24E8, "\u{24ce}"), + (0x24E9, "\u{24cf}"), + (0x2C30, "\u{2c00}"), + (0x2C31, "\u{2c01}"), + (0x2C32, "\u{2c02}"), + (0x2C33, "\u{2c03}"), + (0x2C34, "\u{2c04}"), + (0x2C35, "\u{2c05}"), + (0x2C36, "\u{2c06}"), + (0x2C37, "\u{2c07}"), + (0x2C38, "\u{2c08}"), + (0x2C39, "\u{2c09}"), + (0x2C3A, "\u{2c0a}"), + (0x2C3B, "\u{2c0b}"), + (0x2C3C, "\u{2c0c}"), + (0x2C3D, "\u{2c0d}"), + (0x2C3E, "\u{2c0e}"), + (0x2C3F, "\u{2c0f}"), + (0x2C40, "\u{2c10}"), + (0x2C41, "\u{2c11}"), + (0x2C42, "\u{2c12}"), + (0x2C43, "\u{2c13}"), + (0x2C44, "\u{2c14}"), + (0x2C45, "\u{2c15}"), + (0x2C46, "\u{2c16}"), + (0x2C47, "\u{2c17}"), + (0x2C48, "\u{2c18}"), + (0x2C49, "\u{2c19}"), + (0x2C4A, "\u{2c1a}"), + (0x2C4B, "\u{2c1b}"), + (0x2C4C, "\u{2c1c}"), + (0x2C4D, "\u{2c1d}"), + (0x2C4E, "\u{2c1e}"), + (0x2C4F, "\u{2c1f}"), + (0x2C50, "\u{2c20}"), + (0x2C51, "\u{2c21}"), + (0x2C52, "\u{2c22}"), + (0x2C53, "\u{2c23}"), + (0x2C54, "\u{2c24}"), + (0x2C55, "\u{2c25}"), + (0x2C56, "\u{2c26}"), + (0x2C57, "\u{2c27}"), + (0x2C58, "\u{2c28}"), + (0x2C59, "\u{2c29}"), + (0x2C5A, "\u{2c2a}"), + (0x2C5B, "\u{2c2b}"), + (0x2C5C, "\u{2c2c}"), + (0x2C5D, "\u{2c2d}"), + (0x2C5E, "\u{2c2e}"), + (0x2C5F, "\u{2c2f}"), + (0x2C61, "\u{2c60}"), + (0x2C65, "\u{23a}"), + (0x2C66, "\u{23e}"), + (0x2C68, "\u{2c67}"), + (0x2C6A, "\u{2c69}"), + (0x2C6C, "\u{2c6b}"), + (0x2C73, "\u{2c72}"), + (0x2C76, "\u{2c75}"), + (0x2C81, "\u{2c80}"), + (0x2C83, "\u{2c82}"), + (0x2C85, "\u{2c84}"), + (0x2C87, "\u{2c86}"), + (0x2C89, "\u{2c88}"), + (0x2C8B, "\u{2c8a}"), + (0x2C8D, "\u{2c8c}"), + (0x2C8F, "\u{2c8e}"), + (0x2C91, "\u{2c90}"), + (0x2C93, "\u{2c92}"), + (0x2C95, "\u{2c94}"), + (0x2C97, "\u{2c96}"), + (0x2C99, "\u{2c98}"), + (0x2C9B, "\u{2c9a}"), + (0x2C9D, "\u{2c9c}"), + (0x2C9F, "\u{2c9e}"), + (0x2CA1, "\u{2ca0}"), + (0x2CA3, "\u{2ca2}"), + (0x2CA5, "\u{2ca4}"), + (0x2CA7, "\u{2ca6}"), + (0x2CA9, "\u{2ca8}"), + (0x2CAB, "\u{2caa}"), + (0x2CAD, "\u{2cac}"), + (0x2CAF, "\u{2cae}"), + (0x2CB1, "\u{2cb0}"), + (0x2CB3, "\u{2cb2}"), + (0x2CB5, "\u{2cb4}"), + (0x2CB7, "\u{2cb6}"), + (0x2CB9, "\u{2cb8}"), + (0x2CBB, "\u{2cba}"), + (0x2CBD, "\u{2cbc}"), + (0x2CBF, "\u{2cbe}"), + (0x2CC1, "\u{2cc0}"), + (0x2CC3, "\u{2cc2}"), + (0x2CC5, "\u{2cc4}"), + (0x2CC7, "\u{2cc6}"), + (0x2CC9, "\u{2cc8}"), + (0x2CCB, "\u{2cca}"), + (0x2CCD, "\u{2ccc}"), + (0x2CCF, "\u{2cce}"), + (0x2CD1, "\u{2cd0}"), + (0x2CD3, "\u{2cd2}"), + (0x2CD5, "\u{2cd4}"), + (0x2CD7, "\u{2cd6}"), + (0x2CD9, "\u{2cd8}"), + (0x2CDB, "\u{2cda}"), + (0x2CDD, "\u{2cdc}"), + (0x2CDF, "\u{2cde}"), + (0x2CE1, "\u{2ce0}"), + (0x2CE3, "\u{2ce2}"), + (0x2CEC, "\u{2ceb}"), + (0x2CEE, "\u{2ced}"), + (0x2CF3, "\u{2cf2}"), + (0x2D00, "\u{10a0}"), + (0x2D01, "\u{10a1}"), + (0x2D02, "\u{10a2}"), + (0x2D03, "\u{10a3}"), + (0x2D04, "\u{10a4}"), + (0x2D05, "\u{10a5}"), + (0x2D06, "\u{10a6}"), + (0x2D07, "\u{10a7}"), + (0x2D08, "\u{10a8}"), + (0x2D09, "\u{10a9}"), + (0x2D0A, "\u{10aa}"), + (0x2D0B, "\u{10ab}"), + (0x2D0C, "\u{10ac}"), + (0x2D0D, "\u{10ad}"), + (0x2D0E, "\u{10ae}"), + (0x2D0F, "\u{10af}"), + (0x2D10, "\u{10b0}"), + (0x2D11, "\u{10b1}"), + (0x2D12, "\u{10b2}"), + (0x2D13, "\u{10b3}"), + (0x2D14, "\u{10b4}"), + (0x2D15, "\u{10b5}"), + (0x2D16, "\u{10b6}"), + (0x2D17, "\u{10b7}"), + (0x2D18, "\u{10b8}"), + (0x2D19, "\u{10b9}"), + (0x2D1A, "\u{10ba}"), + (0x2D1B, "\u{10bb}"), + (0x2D1C, "\u{10bc}"), + (0x2D1D, "\u{10bd}"), + (0x2D1E, "\u{10be}"), + (0x2D1F, "\u{10bf}"), + (0x2D20, "\u{10c0}"), + (0x2D21, "\u{10c1}"), + (0x2D22, "\u{10c2}"), + (0x2D23, "\u{10c3}"), + (0x2D24, "\u{10c4}"), + (0x2D25, "\u{10c5}"), + (0x2D27, "\u{10c7}"), + (0x2D2D, "\u{10cd}"), + (0xA641, "\u{a640}"), + (0xA643, "\u{a642}"), + (0xA645, "\u{a644}"), + (0xA647, "\u{a646}"), + (0xA649, "\u{a648}"), + (0xA64B, "\u{a64a}"), + (0xA64D, "\u{a64c}"), + (0xA64F, "\u{a64e}"), + (0xA651, "\u{a650}"), + (0xA653, "\u{a652}"), + (0xA655, "\u{a654}"), + (0xA657, "\u{a656}"), + (0xA659, "\u{a658}"), + (0xA65B, "\u{a65a}"), + (0xA65D, "\u{a65c}"), + (0xA65F, "\u{a65e}"), + (0xA661, "\u{a660}"), + (0xA663, "\u{a662}"), + (0xA665, "\u{a664}"), + (0xA667, "\u{a666}"), + (0xA669, "\u{a668}"), + (0xA66B, "\u{a66a}"), + (0xA66D, "\u{a66c}"), + (0xA681, "\u{a680}"), + (0xA683, "\u{a682}"), + (0xA685, "\u{a684}"), + (0xA687, "\u{a686}"), + (0xA689, "\u{a688}"), + (0xA68B, "\u{a68a}"), + (0xA68D, "\u{a68c}"), + (0xA68F, "\u{a68e}"), + (0xA691, "\u{a690}"), + (0xA693, "\u{a692}"), + (0xA695, "\u{a694}"), + (0xA697, "\u{a696}"), + (0xA699, "\u{a698}"), + (0xA69B, "\u{a69a}"), + (0xA723, "\u{a722}"), + (0xA725, "\u{a724}"), + (0xA727, "\u{a726}"), + (0xA729, "\u{a728}"), + (0xA72B, "\u{a72a}"), + (0xA72D, "\u{a72c}"), + (0xA72F, "\u{a72e}"), + (0xA733, "\u{a732}"), + (0xA735, "\u{a734}"), + (0xA737, "\u{a736}"), + (0xA739, "\u{a738}"), + (0xA73B, "\u{a73a}"), + (0xA73D, "\u{a73c}"), + (0xA73F, "\u{a73e}"), + (0xA741, "\u{a740}"), + (0xA743, "\u{a742}"), + (0xA745, "\u{a744}"), + (0xA747, "\u{a746}"), + (0xA749, "\u{a748}"), + (0xA74B, "\u{a74a}"), + (0xA74D, "\u{a74c}"), + (0xA74F, "\u{a74e}"), + (0xA751, "\u{a750}"), + (0xA753, "\u{a752}"), + (0xA755, "\u{a754}"), + (0xA757, "\u{a756}"), + (0xA759, "\u{a758}"), + (0xA75B, "\u{a75a}"), + (0xA75D, "\u{a75c}"), + (0xA75F, "\u{a75e}"), + (0xA761, "\u{a760}"), + (0xA763, "\u{a762}"), + (0xA765, "\u{a764}"), + (0xA767, "\u{a766}"), + (0xA769, "\u{a768}"), + (0xA76B, "\u{a76a}"), + (0xA76D, "\u{a76c}"), + (0xA76F, "\u{a76e}"), + (0xA77A, "\u{a779}"), + (0xA77C, "\u{a77b}"), + (0xA77F, "\u{a77e}"), + (0xA781, "\u{a780}"), + (0xA783, "\u{a782}"), + (0xA785, "\u{a784}"), + (0xA787, "\u{a786}"), + (0xA78C, "\u{a78b}"), + (0xA791, "\u{a790}"), + (0xA793, "\u{a792}"), + (0xA794, "\u{a7c4}"), + (0xA797, "\u{a796}"), + (0xA799, "\u{a798}"), + (0xA79B, "\u{a79a}"), + (0xA79D, "\u{a79c}"), + (0xA79F, "\u{a79e}"), + (0xA7A1, "\u{a7a0}"), + (0xA7A3, "\u{a7a2}"), + (0xA7A5, "\u{a7a4}"), + (0xA7A7, "\u{a7a6}"), + (0xA7A9, "\u{a7a8}"), + (0xA7B5, "\u{a7b4}"), + (0xA7B7, "\u{a7b6}"), + (0xA7B9, "\u{a7b8}"), + (0xA7BB, "\u{a7ba}"), + (0xA7BD, "\u{a7bc}"), + (0xA7BF, "\u{a7be}"), + (0xA7C1, "\u{a7c0}"), + (0xA7C3, "\u{a7c2}"), + (0xA7C8, "\u{a7c7}"), + (0xA7CA, "\u{a7c9}"), + (0xA7D1, "\u{a7d0}"), + (0xA7D7, "\u{a7d6}"), + (0xA7D9, "\u{a7d8}"), + (0xA7F6, "\u{a7f5}"), + (0xAB53, "\u{a7b3}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{46}\u{46}"), + (0xFB01, "\u{46}\u{49}"), + (0xFB02, "\u{46}\u{4c}"), + (0xFB03, "\u{46}\u{46}\u{49}"), + (0xFB04, "\u{46}\u{46}\u{4c}"), + (0xFB05, "\u{53}\u{54}"), + (0xFB06, "\u{53}\u{54}"), + (0xFB13, "\u{544}\u{546}"), + (0xFB14, "\u{544}\u{535}"), + (0xFB15, "\u{544}\u{53b}"), + (0xFB16, "\u{54e}\u{546}"), + (0xFB17, "\u{544}\u{53d}"), + (0xFF41, "\u{ff21}"), + (0xFF42, "\u{ff22}"), + (0xFF43, "\u{ff23}"), + (0xFF44, "\u{ff24}"), + (0xFF45, "\u{ff25}"), + (0xFF46, "\u{ff26}"), + (0xFF47, "\u{ff27}"), + (0xFF48, "\u{ff28}"), + (0xFF49, "\u{ff29}"), + (0xFF4A, "\u{ff2a}"), + (0xFF4B, "\u{ff2b}"), + (0xFF4C, "\u{ff2c}"), + (0xFF4D, "\u{ff2d}"), + (0xFF4E, "\u{ff2e}"), + (0xFF4F, "\u{ff2f}"), + (0xFF50, "\u{ff30}"), + (0xFF51, "\u{ff31}"), + (0xFF52, "\u{ff32}"), + (0xFF53, "\u{ff33}"), + (0xFF54, "\u{ff34}"), + (0xFF55, "\u{ff35}"), + (0xFF56, "\u{ff36}"), + (0xFF57, "\u{ff37}"), + (0xFF58, "\u{ff38}"), + (0xFF59, "\u{ff39}"), + (0xFF5A, "\u{ff3a}"), + (0x10428, "\u{10400}"), + (0x10429, "\u{10401}"), + (0x1042A, "\u{10402}"), + (0x1042B, "\u{10403}"), + (0x1042C, "\u{10404}"), + (0x1042D, "\u{10405}"), + (0x1042E, "\u{10406}"), + (0x1042F, "\u{10407}"), + (0x10430, "\u{10408}"), + (0x10431, "\u{10409}"), + (0x10432, "\u{1040a}"), + (0x10433, "\u{1040b}"), + (0x10434, "\u{1040c}"), + (0x10435, "\u{1040d}"), + (0x10436, "\u{1040e}"), + (0x10437, "\u{1040f}"), + (0x10438, "\u{10410}"), + (0x10439, "\u{10411}"), + (0x1043A, "\u{10412}"), + (0x1043B, "\u{10413}"), + (0x1043C, "\u{10414}"), + (0x1043D, "\u{10415}"), + (0x1043E, "\u{10416}"), + (0x1043F, "\u{10417}"), + (0x10440, "\u{10418}"), + (0x10441, "\u{10419}"), + (0x10442, "\u{1041a}"), + (0x10443, "\u{1041b}"), + (0x10444, "\u{1041c}"), + (0x10445, "\u{1041d}"), + (0x10446, "\u{1041e}"), + (0x10447, "\u{1041f}"), + (0x10448, "\u{10420}"), + (0x10449, "\u{10421}"), + (0x1044A, "\u{10422}"), + (0x1044B, "\u{10423}"), + (0x1044C, "\u{10424}"), + (0x1044D, "\u{10425}"), + (0x1044E, "\u{10426}"), + (0x1044F, "\u{10427}"), + (0x104D8, "\u{104b0}"), + (0x104D9, "\u{104b1}"), + (0x104DA, "\u{104b2}"), + (0x104DB, "\u{104b3}"), + (0x104DC, "\u{104b4}"), + (0x104DD, "\u{104b5}"), + (0x104DE, "\u{104b6}"), + (0x104DF, "\u{104b7}"), + (0x104E0, "\u{104b8}"), + (0x104E1, "\u{104b9}"), + (0x104E2, "\u{104ba}"), + (0x104E3, "\u{104bb}"), + (0x104E4, "\u{104bc}"), + (0x104E5, "\u{104bd}"), + (0x104E6, "\u{104be}"), + (0x104E7, "\u{104bf}"), + (0x104E8, "\u{104c0}"), + (0x104E9, "\u{104c1}"), + (0x104EA, "\u{104c2}"), + (0x104EB, "\u{104c3}"), + (0x104EC, "\u{104c4}"), + (0x104ED, "\u{104c5}"), + (0x104EE, "\u{104c6}"), + (0x104EF, "\u{104c7}"), + (0x104F0, "\u{104c8}"), + (0x104F1, "\u{104c9}"), + (0x104F2, "\u{104ca}"), + (0x104F3, "\u{104cb}"), + (0x104F4, "\u{104cc}"), + (0x104F5, "\u{104cd}"), + (0x104F6, "\u{104ce}"), + (0x104F7, "\u{104cf}"), + (0x104F8, "\u{104d0}"), + (0x104F9, "\u{104d1}"), + (0x104FA, "\u{104d2}"), + (0x104FB, "\u{104d3}"), + (0x10597, "\u{10570}"), + (0x10598, "\u{10571}"), + (0x10599, "\u{10572}"), + (0x1059A, "\u{10573}"), + (0x1059B, "\u{10574}"), + (0x1059C, "\u{10575}"), + (0x1059D, "\u{10576}"), + (0x1059E, "\u{10577}"), + (0x1059F, "\u{10578}"), + (0x105A0, "\u{10579}"), + (0x105A1, "\u{1057a}"), + (0x105A3, "\u{1057c}"), + (0x105A4, "\u{1057d}"), + (0x105A5, "\u{1057e}"), + (0x105A6, "\u{1057f}"), + (0x105A7, "\u{10580}"), + (0x105A8, "\u{10581}"), + (0x105A9, "\u{10582}"), + (0x105AA, "\u{10583}"), + (0x105AB, "\u{10584}"), + (0x105AC, "\u{10585}"), + (0x105AD, "\u{10586}"), + (0x105AE, "\u{10587}"), + (0x105AF, "\u{10588}"), + (0x105B0, "\u{10589}"), + (0x105B1, "\u{1058a}"), + (0x105B3, "\u{1058c}"), + (0x105B4, "\u{1058d}"), + (0x105B5, "\u{1058e}"), + (0x105B6, "\u{1058f}"), + (0x105B7, "\u{10590}"), + (0x105B8, "\u{10591}"), + (0x105B9, "\u{10592}"), + (0x105BB, "\u{10594}"), + (0x105BC, "\u{10595}"), + (0x10CC0, "\u{10c80}"), + (0x10CC1, "\u{10c81}"), + (0x10CC2, "\u{10c82}"), + (0x10CC3, "\u{10c83}"), + (0x10CC4, "\u{10c84}"), + (0x10CC5, "\u{10c85}"), + (0x10CC6, "\u{10c86}"), + (0x10CC7, "\u{10c87}"), + (0x10CC8, "\u{10c88}"), + (0x10CC9, "\u{10c89}"), + (0x10CCA, "\u{10c8a}"), + (0x10CCB, "\u{10c8b}"), + (0x10CCC, "\u{10c8c}"), + (0x10CCD, "\u{10c8d}"), + (0x10CCE, "\u{10c8e}"), + (0x10CCF, "\u{10c8f}"), + (0x10CD0, "\u{10c90}"), + (0x10CD1, "\u{10c91}"), + (0x10CD2, "\u{10c92}"), + (0x10CD3, "\u{10c93}"), + (0x10CD4, "\u{10c94}"), + (0x10CD5, "\u{10c95}"), + (0x10CD6, "\u{10c96}"), + (0x10CD7, "\u{10c97}"), + (0x10CD8, "\u{10c98}"), + (0x10CD9, "\u{10c99}"), + (0x10CDA, "\u{10c9a}"), + (0x10CDB, "\u{10c9b}"), + (0x10CDC, "\u{10c9c}"), + (0x10CDD, "\u{10c9d}"), + (0x10CDE, "\u{10c9e}"), + (0x10CDF, "\u{10c9f}"), + (0x10CE0, "\u{10ca0}"), + (0x10CE1, "\u{10ca1}"), + (0x10CE2, "\u{10ca2}"), + (0x10CE3, "\u{10ca3}"), + (0x10CE4, "\u{10ca4}"), + (0x10CE5, "\u{10ca5}"), + (0x10CE6, "\u{10ca6}"), + (0x10CE7, "\u{10ca7}"), + (0x10CE8, "\u{10ca8}"), + (0x10CE9, "\u{10ca9}"), + (0x10CEA, "\u{10caa}"), + (0x10CEB, "\u{10cab}"), + (0x10CEC, "\u{10cac}"), + (0x10CED, "\u{10cad}"), + (0x10CEE, "\u{10cae}"), + (0x10CEF, "\u{10caf}"), + (0x10CF0, "\u{10cb0}"), + (0x10CF1, "\u{10cb1}"), + (0x10CF2, "\u{10cb2}"), + (0x118C0, "\u{118a0}"), + (0x118C1, "\u{118a1}"), + (0x118C2, "\u{118a2}"), + (0x118C3, "\u{118a3}"), + (0x118C4, "\u{118a4}"), + (0x118C5, "\u{118a5}"), + (0x118C6, "\u{118a6}"), + (0x118C7, "\u{118a7}"), + (0x118C8, "\u{118a8}"), + (0x118C9, "\u{118a9}"), + (0x118CA, "\u{118aa}"), + (0x118CB, "\u{118ab}"), + (0x118CC, "\u{118ac}"), + (0x118CD, "\u{118ad}"), + (0x118CE, "\u{118ae}"), + (0x118CF, "\u{118af}"), + (0x118D0, "\u{118b0}"), + (0x118D1, "\u{118b1}"), + (0x118D2, "\u{118b2}"), + (0x118D3, "\u{118b3}"), + (0x118D4, "\u{118b4}"), + (0x118D5, "\u{118b5}"), + (0x118D6, "\u{118b6}"), + (0x118D7, "\u{118b7}"), + (0x118D8, "\u{118b8}"), + (0x118D9, "\u{118b9}"), + (0x118DA, "\u{118ba}"), + (0x118DB, "\u{118bb}"), + (0x118DC, "\u{118bc}"), + (0x118DD, "\u{118bd}"), + (0x118DE, "\u{118be}"), + (0x118DF, "\u{118bf}"), + (0x16E60, "\u{16e40}"), + (0x16E61, "\u{16e41}"), + (0x16E62, "\u{16e42}"), + (0x16E63, "\u{16e43}"), + (0x16E64, "\u{16e44}"), + (0x16E65, "\u{16e45}"), + (0x16E66, "\u{16e46}"), + (0x16E67, "\u{16e47}"), + (0x16E68, "\u{16e48}"), + (0x16E69, "\u{16e49}"), + (0x16E6A, "\u{16e4a}"), + (0x16E6B, "\u{16e4b}"), + (0x16E6C, "\u{16e4c}"), + (0x16E6D, "\u{16e4d}"), + (0x16E6E, "\u{16e4e}"), + (0x16E6F, "\u{16e4f}"), + (0x16E70, "\u{16e50}"), + (0x16E71, "\u{16e51}"), + (0x16E72, "\u{16e52}"), + (0x16E73, "\u{16e53}"), + (0x16E74, "\u{16e54}"), + (0x16E75, "\u{16e55}"), + (0x16E76, "\u{16e56}"), + (0x16E77, "\u{16e57}"), + (0x16E78, "\u{16e58}"), + (0x16E79, "\u{16e59}"), + (0x16E7A, "\u{16e5a}"), + (0x16E7B, "\u{16e5b}"), + (0x16E7C, "\u{16e5c}"), + (0x16E7D, "\u{16e5d}"), + (0x16E7E, "\u{16e5e}"), + (0x16E7F, "\u{16e5f}"), + (0x1E922, "\u{1e900}"), + (0x1E923, "\u{1e901}"), + (0x1E924, "\u{1e902}"), + (0x1E925, "\u{1e903}"), + (0x1E926, "\u{1e904}"), + (0x1E927, "\u{1e905}"), + (0x1E928, "\u{1e906}"), + (0x1E929, "\u{1e907}"), + (0x1E92A, "\u{1e908}"), + (0x1E92B, "\u{1e909}"), + (0x1E92C, "\u{1e90a}"), + (0x1E92D, "\u{1e90b}"), + (0x1E92E, "\u{1e90c}"), + (0x1E92F, "\u{1e90d}"), + (0x1E930, "\u{1e90e}"), + (0x1E931, "\u{1e90f}"), + (0x1E932, "\u{1e910}"), + (0x1E933, "\u{1e911}"), + (0x1E934, "\u{1e912}"), + (0x1E935, "\u{1e913}"), + (0x1E936, "\u{1e914}"), + (0x1E937, "\u{1e915}"), + (0x1E938, "\u{1e916}"), + (0x1E939, "\u{1e917}"), + (0x1E93A, "\u{1e918}"), + (0x1E93B, "\u{1e919}"), + (0x1E93C, "\u{1e91a}"), + (0x1E93D, "\u{1e91b}"), + (0x1E93E, "\u{1e91c}"), + (0x1E93F, "\u{1e91d}"), + (0x1E940, "\u{1e91e}"), + (0x1E941, "\u{1e91f}"), + (0x1E942, "\u{1e920}"), + (0x1E943, "\u{1e921}"), +] diff --git a/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_1_0.in b/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_1_0.in new file mode 100644 index 0000000000..7638290364 --- /dev/null +++ b/crates/sqlbuild-core/src/text/upper_mappings/unicode_15_1_0.in @@ -0,0 +1,1529 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.upper()` changes in Unicode 15.1.0, in code point order. +&[ + (0x0061, "\u{41}"), + (0x0062, "\u{42}"), + (0x0063, "\u{43}"), + (0x0064, "\u{44}"), + (0x0065, "\u{45}"), + (0x0066, "\u{46}"), + (0x0067, "\u{47}"), + (0x0068, "\u{48}"), + (0x0069, "\u{49}"), + (0x006A, "\u{4a}"), + (0x006B, "\u{4b}"), + (0x006C, "\u{4c}"), + (0x006D, "\u{4d}"), + (0x006E, "\u{4e}"), + (0x006F, "\u{4f}"), + (0x0070, "\u{50}"), + (0x0071, "\u{51}"), + (0x0072, "\u{52}"), + (0x0073, "\u{53}"), + (0x0074, "\u{54}"), + (0x0075, "\u{55}"), + (0x0076, "\u{56}"), + (0x0077, "\u{57}"), + (0x0078, "\u{58}"), + (0x0079, "\u{59}"), + (0x007A, "\u{5a}"), + (0x00B5, "\u{39c}"), + (0x00DF, "\u{53}\u{53}"), + (0x00E0, "\u{c0}"), + (0x00E1, "\u{c1}"), + (0x00E2, "\u{c2}"), + (0x00E3, "\u{c3}"), + (0x00E4, "\u{c4}"), + (0x00E5, "\u{c5}"), + (0x00E6, "\u{c6}"), + (0x00E7, "\u{c7}"), + (0x00E8, "\u{c8}"), + (0x00E9, "\u{c9}"), + (0x00EA, "\u{ca}"), + (0x00EB, "\u{cb}"), + (0x00EC, "\u{cc}"), + (0x00ED, "\u{cd}"), + (0x00EE, "\u{ce}"), + (0x00EF, "\u{cf}"), + (0x00F0, "\u{d0}"), + (0x00F1, "\u{d1}"), + (0x00F2, "\u{d2}"), + (0x00F3, "\u{d3}"), + (0x00F4, "\u{d4}"), + (0x00F5, "\u{d5}"), + (0x00F6, "\u{d6}"), + (0x00F8, "\u{d8}"), + (0x00F9, "\u{d9}"), + (0x00FA, "\u{da}"), + (0x00FB, "\u{db}"), + (0x00FC, "\u{dc}"), + (0x00FD, "\u{dd}"), + (0x00FE, "\u{de}"), + (0x00FF, "\u{178}"), + (0x0101, "\u{100}"), + (0x0103, "\u{102}"), + (0x0105, "\u{104}"), + (0x0107, "\u{106}"), + (0x0109, "\u{108}"), + (0x010B, "\u{10a}"), + (0x010D, "\u{10c}"), + (0x010F, "\u{10e}"), + (0x0111, "\u{110}"), + (0x0113, "\u{112}"), + (0x0115, "\u{114}"), + (0x0117, "\u{116}"), + (0x0119, "\u{118}"), + (0x011B, "\u{11a}"), + (0x011D, "\u{11c}"), + (0x011F, "\u{11e}"), + (0x0121, "\u{120}"), + (0x0123, "\u{122}"), + (0x0125, "\u{124}"), + (0x0127, "\u{126}"), + (0x0129, "\u{128}"), + (0x012B, "\u{12a}"), + (0x012D, "\u{12c}"), + (0x012F, "\u{12e}"), + (0x0131, "\u{49}"), + (0x0133, "\u{132}"), + (0x0135, "\u{134}"), + (0x0137, "\u{136}"), + (0x013A, "\u{139}"), + (0x013C, "\u{13b}"), + (0x013E, "\u{13d}"), + (0x0140, "\u{13f}"), + (0x0142, "\u{141}"), + (0x0144, "\u{143}"), + (0x0146, "\u{145}"), + (0x0148, "\u{147}"), + (0x0149, "\u{2bc}\u{4e}"), + (0x014B, "\u{14a}"), + (0x014D, "\u{14c}"), + (0x014F, "\u{14e}"), + (0x0151, "\u{150}"), + (0x0153, "\u{152}"), + (0x0155, "\u{154}"), + (0x0157, "\u{156}"), + (0x0159, "\u{158}"), + (0x015B, "\u{15a}"), + (0x015D, "\u{15c}"), + (0x015F, "\u{15e}"), + (0x0161, "\u{160}"), + (0x0163, "\u{162}"), + (0x0165, "\u{164}"), + (0x0167, "\u{166}"), + (0x0169, "\u{168}"), + (0x016B, "\u{16a}"), + (0x016D, "\u{16c}"), + (0x016F, "\u{16e}"), + (0x0171, "\u{170}"), + (0x0173, "\u{172}"), + (0x0175, "\u{174}"), + (0x0177, "\u{176}"), + (0x017A, "\u{179}"), + (0x017C, "\u{17b}"), + (0x017E, "\u{17d}"), + (0x017F, "\u{53}"), + (0x0180, "\u{243}"), + (0x0183, "\u{182}"), + (0x0185, "\u{184}"), + (0x0188, "\u{187}"), + (0x018C, "\u{18b}"), + (0x0192, "\u{191}"), + (0x0195, "\u{1f6}"), + (0x0199, "\u{198}"), + (0x019A, "\u{23d}"), + (0x019E, "\u{220}"), + (0x01A1, "\u{1a0}"), + (0x01A3, "\u{1a2}"), + (0x01A5, "\u{1a4}"), + (0x01A8, "\u{1a7}"), + (0x01AD, "\u{1ac}"), + (0x01B0, "\u{1af}"), + (0x01B4, "\u{1b3}"), + (0x01B6, "\u{1b5}"), + (0x01B9, "\u{1b8}"), + (0x01BD, "\u{1bc}"), + (0x01BF, "\u{1f7}"), + (0x01C5, "\u{1c4}"), + (0x01C6, "\u{1c4}"), + (0x01C8, "\u{1c7}"), + (0x01C9, "\u{1c7}"), + (0x01CB, "\u{1ca}"), + (0x01CC, "\u{1ca}"), + (0x01CE, "\u{1cd}"), + (0x01D0, "\u{1cf}"), + (0x01D2, "\u{1d1}"), + (0x01D4, "\u{1d3}"), + (0x01D6, "\u{1d5}"), + (0x01D8, "\u{1d7}"), + (0x01DA, "\u{1d9}"), + (0x01DC, "\u{1db}"), + (0x01DD, "\u{18e}"), + (0x01DF, "\u{1de}"), + (0x01E1, "\u{1e0}"), + (0x01E3, "\u{1e2}"), + (0x01E5, "\u{1e4}"), + (0x01E7, "\u{1e6}"), + (0x01E9, "\u{1e8}"), + (0x01EB, "\u{1ea}"), + (0x01ED, "\u{1ec}"), + (0x01EF, "\u{1ee}"), + (0x01F0, "\u{4a}\u{30c}"), + (0x01F2, "\u{1f1}"), + (0x01F3, "\u{1f1}"), + (0x01F5, "\u{1f4}"), + (0x01F9, "\u{1f8}"), + (0x01FB, "\u{1fa}"), + (0x01FD, "\u{1fc}"), + (0x01FF, "\u{1fe}"), + (0x0201, "\u{200}"), + (0x0203, "\u{202}"), + (0x0205, "\u{204}"), + (0x0207, "\u{206}"), + (0x0209, "\u{208}"), + (0x020B, "\u{20a}"), + (0x020D, "\u{20c}"), + (0x020F, "\u{20e}"), + (0x0211, "\u{210}"), + (0x0213, "\u{212}"), + (0x0215, "\u{214}"), + (0x0217, "\u{216}"), + (0x0219, "\u{218}"), + (0x021B, "\u{21a}"), + (0x021D, "\u{21c}"), + (0x021F, "\u{21e}"), + (0x0223, "\u{222}"), + (0x0225, "\u{224}"), + (0x0227, "\u{226}"), + (0x0229, "\u{228}"), + (0x022B, "\u{22a}"), + (0x022D, "\u{22c}"), + (0x022F, "\u{22e}"), + (0x0231, "\u{230}"), + (0x0233, "\u{232}"), + (0x023C, "\u{23b}"), + (0x023F, "\u{2c7e}"), + (0x0240, "\u{2c7f}"), + (0x0242, "\u{241}"), + (0x0247, "\u{246}"), + (0x0249, "\u{248}"), + (0x024B, "\u{24a}"), + (0x024D, "\u{24c}"), + (0x024F, "\u{24e}"), + (0x0250, "\u{2c6f}"), + (0x0251, "\u{2c6d}"), + (0x0252, "\u{2c70}"), + (0x0253, "\u{181}"), + (0x0254, "\u{186}"), + (0x0256, "\u{189}"), + (0x0257, "\u{18a}"), + (0x0259, "\u{18f}"), + (0x025B, "\u{190}"), + (0x025C, "\u{a7ab}"), + (0x0260, "\u{193}"), + (0x0261, "\u{a7ac}"), + (0x0263, "\u{194}"), + (0x0265, "\u{a78d}"), + (0x0266, "\u{a7aa}"), + (0x0268, "\u{197}"), + (0x0269, "\u{196}"), + (0x026A, "\u{a7ae}"), + (0x026B, "\u{2c62}"), + (0x026C, "\u{a7ad}"), + (0x026F, "\u{19c}"), + (0x0271, "\u{2c6e}"), + (0x0272, "\u{19d}"), + (0x0275, "\u{19f}"), + (0x027D, "\u{2c64}"), + (0x0280, "\u{1a6}"), + (0x0282, "\u{a7c5}"), + (0x0283, "\u{1a9}"), + (0x0287, "\u{a7b1}"), + (0x0288, "\u{1ae}"), + (0x0289, "\u{244}"), + (0x028A, "\u{1b1}"), + (0x028B, "\u{1b2}"), + (0x028C, "\u{245}"), + (0x0292, "\u{1b7}"), + (0x029D, "\u{a7b2}"), + (0x029E, "\u{a7b0}"), + (0x0345, "\u{399}"), + (0x0371, "\u{370}"), + (0x0373, "\u{372}"), + (0x0377, "\u{376}"), + (0x037B, "\u{3fd}"), + (0x037C, "\u{3fe}"), + (0x037D, "\u{3ff}"), + (0x0390, "\u{399}\u{308}\u{301}"), + (0x03AC, "\u{386}"), + (0x03AD, "\u{388}"), + (0x03AE, "\u{389}"), + (0x03AF, "\u{38a}"), + (0x03B0, "\u{3a5}\u{308}\u{301}"), + (0x03B1, "\u{391}"), + (0x03B2, "\u{392}"), + (0x03B3, "\u{393}"), + (0x03B4, "\u{394}"), + (0x03B5, "\u{395}"), + (0x03B6, "\u{396}"), + (0x03B7, "\u{397}"), + (0x03B8, "\u{398}"), + (0x03B9, "\u{399}"), + (0x03BA, "\u{39a}"), + (0x03BB, "\u{39b}"), + (0x03BC, "\u{39c}"), + (0x03BD, "\u{39d}"), + (0x03BE, "\u{39e}"), + (0x03BF, "\u{39f}"), + (0x03C0, "\u{3a0}"), + (0x03C1, "\u{3a1}"), + (0x03C2, "\u{3a3}"), + (0x03C3, "\u{3a3}"), + (0x03C4, "\u{3a4}"), + (0x03C5, "\u{3a5}"), + (0x03C6, "\u{3a6}"), + (0x03C7, "\u{3a7}"), + (0x03C8, "\u{3a8}"), + (0x03C9, "\u{3a9}"), + (0x03CA, "\u{3aa}"), + (0x03CB, "\u{3ab}"), + (0x03CC, "\u{38c}"), + (0x03CD, "\u{38e}"), + (0x03CE, "\u{38f}"), + (0x03D0, "\u{392}"), + (0x03D1, "\u{398}"), + (0x03D5, "\u{3a6}"), + (0x03D6, "\u{3a0}"), + (0x03D7, "\u{3cf}"), + (0x03D9, "\u{3d8}"), + (0x03DB, "\u{3da}"), + (0x03DD, "\u{3dc}"), + (0x03DF, "\u{3de}"), + (0x03E1, "\u{3e0}"), + (0x03E3, "\u{3e2}"), + (0x03E5, "\u{3e4}"), + (0x03E7, "\u{3e6}"), + (0x03E9, "\u{3e8}"), + (0x03EB, "\u{3ea}"), + (0x03ED, "\u{3ec}"), + (0x03EF, "\u{3ee}"), + (0x03F0, "\u{39a}"), + (0x03F1, "\u{3a1}"), + (0x03F2, "\u{3f9}"), + (0x03F3, "\u{37f}"), + (0x03F5, "\u{395}"), + (0x03F8, "\u{3f7}"), + (0x03FB, "\u{3fa}"), + (0x0430, "\u{410}"), + (0x0431, "\u{411}"), + (0x0432, "\u{412}"), + (0x0433, "\u{413}"), + (0x0434, "\u{414}"), + (0x0435, "\u{415}"), + (0x0436, "\u{416}"), + (0x0437, "\u{417}"), + (0x0438, "\u{418}"), + (0x0439, "\u{419}"), + (0x043A, "\u{41a}"), + (0x043B, "\u{41b}"), + (0x043C, "\u{41c}"), + (0x043D, "\u{41d}"), + (0x043E, "\u{41e}"), + (0x043F, "\u{41f}"), + (0x0440, "\u{420}"), + (0x0441, "\u{421}"), + (0x0442, "\u{422}"), + (0x0443, "\u{423}"), + (0x0444, "\u{424}"), + (0x0445, "\u{425}"), + (0x0446, "\u{426}"), + (0x0447, "\u{427}"), + (0x0448, "\u{428}"), + (0x0449, "\u{429}"), + (0x044A, "\u{42a}"), + (0x044B, "\u{42b}"), + (0x044C, "\u{42c}"), + (0x044D, "\u{42d}"), + (0x044E, "\u{42e}"), + (0x044F, "\u{42f}"), + (0x0450, "\u{400}"), + (0x0451, "\u{401}"), + (0x0452, "\u{402}"), + (0x0453, "\u{403}"), + (0x0454, "\u{404}"), + (0x0455, "\u{405}"), + (0x0456, "\u{406}"), + (0x0457, "\u{407}"), + (0x0458, "\u{408}"), + (0x0459, "\u{409}"), + (0x045A, "\u{40a}"), + (0x045B, "\u{40b}"), + (0x045C, "\u{40c}"), + (0x045D, "\u{40d}"), + (0x045E, "\u{40e}"), + (0x045F, "\u{40f}"), + (0x0461, "\u{460}"), + (0x0463, "\u{462}"), + (0x0465, "\u{464}"), + (0x0467, "\u{466}"), + (0x0469, "\u{468}"), + (0x046B, "\u{46a}"), + (0x046D, "\u{46c}"), + (0x046F, "\u{46e}"), + (0x0471, "\u{470}"), + (0x0473, "\u{472}"), + (0x0475, "\u{474}"), + (0x0477, "\u{476}"), + (0x0479, "\u{478}"), + (0x047B, "\u{47a}"), + (0x047D, "\u{47c}"), + (0x047F, "\u{47e}"), + (0x0481, "\u{480}"), + (0x048B, "\u{48a}"), + (0x048D, "\u{48c}"), + (0x048F, "\u{48e}"), + (0x0491, "\u{490}"), + (0x0493, "\u{492}"), + (0x0495, "\u{494}"), + (0x0497, "\u{496}"), + (0x0499, "\u{498}"), + (0x049B, "\u{49a}"), + (0x049D, "\u{49c}"), + (0x049F, "\u{49e}"), + (0x04A1, "\u{4a0}"), + (0x04A3, "\u{4a2}"), + (0x04A5, "\u{4a4}"), + (0x04A7, "\u{4a6}"), + (0x04A9, "\u{4a8}"), + (0x04AB, "\u{4aa}"), + (0x04AD, "\u{4ac}"), + (0x04AF, "\u{4ae}"), + (0x04B1, "\u{4b0}"), + (0x04B3, "\u{4b2}"), + (0x04B5, "\u{4b4}"), + (0x04B7, "\u{4b6}"), + (0x04B9, "\u{4b8}"), + (0x04BB, "\u{4ba}"), + (0x04BD, "\u{4bc}"), + (0x04BF, "\u{4be}"), + (0x04C2, "\u{4c1}"), + (0x04C4, "\u{4c3}"), + (0x04C6, "\u{4c5}"), + (0x04C8, "\u{4c7}"), + (0x04CA, "\u{4c9}"), + (0x04CC, "\u{4cb}"), + (0x04CE, "\u{4cd}"), + (0x04CF, "\u{4c0}"), + (0x04D1, "\u{4d0}"), + (0x04D3, "\u{4d2}"), + (0x04D5, "\u{4d4}"), + (0x04D7, "\u{4d6}"), + (0x04D9, "\u{4d8}"), + (0x04DB, "\u{4da}"), + (0x04DD, "\u{4dc}"), + (0x04DF, "\u{4de}"), + (0x04E1, "\u{4e0}"), + (0x04E3, "\u{4e2}"), + (0x04E5, "\u{4e4}"), + (0x04E7, "\u{4e6}"), + (0x04E9, "\u{4e8}"), + (0x04EB, "\u{4ea}"), + (0x04ED, "\u{4ec}"), + (0x04EF, "\u{4ee}"), + (0x04F1, "\u{4f0}"), + (0x04F3, "\u{4f2}"), + (0x04F5, "\u{4f4}"), + (0x04F7, "\u{4f6}"), + (0x04F9, "\u{4f8}"), + (0x04FB, "\u{4fa}"), + (0x04FD, "\u{4fc}"), + (0x04FF, "\u{4fe}"), + (0x0501, "\u{500}"), + (0x0503, "\u{502}"), + (0x0505, "\u{504}"), + (0x0507, "\u{506}"), + (0x0509, "\u{508}"), + (0x050B, "\u{50a}"), + (0x050D, "\u{50c}"), + (0x050F, "\u{50e}"), + (0x0511, "\u{510}"), + (0x0513, "\u{512}"), + (0x0515, "\u{514}"), + (0x0517, "\u{516}"), + (0x0519, "\u{518}"), + (0x051B, "\u{51a}"), + (0x051D, "\u{51c}"), + (0x051F, "\u{51e}"), + (0x0521, "\u{520}"), + (0x0523, "\u{522}"), + (0x0525, "\u{524}"), + (0x0527, "\u{526}"), + (0x0529, "\u{528}"), + (0x052B, "\u{52a}"), + (0x052D, "\u{52c}"), + (0x052F, "\u{52e}"), + (0x0561, "\u{531}"), + (0x0562, "\u{532}"), + (0x0563, "\u{533}"), + (0x0564, "\u{534}"), + (0x0565, "\u{535}"), + (0x0566, "\u{536}"), + (0x0567, "\u{537}"), + (0x0568, "\u{538}"), + (0x0569, "\u{539}"), + (0x056A, "\u{53a}"), + (0x056B, "\u{53b}"), + (0x056C, "\u{53c}"), + (0x056D, "\u{53d}"), + (0x056E, "\u{53e}"), + (0x056F, "\u{53f}"), + (0x0570, "\u{540}"), + (0x0571, "\u{541}"), + (0x0572, "\u{542}"), + (0x0573, "\u{543}"), + (0x0574, "\u{544}"), + (0x0575, "\u{545}"), + (0x0576, "\u{546}"), + (0x0577, "\u{547}"), + (0x0578, "\u{548}"), + (0x0579, "\u{549}"), + (0x057A, "\u{54a}"), + (0x057B, "\u{54b}"), + (0x057C, "\u{54c}"), + (0x057D, "\u{54d}"), + (0x057E, "\u{54e}"), + (0x057F, "\u{54f}"), + (0x0580, "\u{550}"), + (0x0581, "\u{551}"), + (0x0582, "\u{552}"), + (0x0583, "\u{553}"), + (0x0584, "\u{554}"), + (0x0585, "\u{555}"), + (0x0586, "\u{556}"), + (0x0587, "\u{535}\u{552}"), + (0x10D0, "\u{1c90}"), + (0x10D1, "\u{1c91}"), + (0x10D2, "\u{1c92}"), + (0x10D3, "\u{1c93}"), + (0x10D4, "\u{1c94}"), + (0x10D5, "\u{1c95}"), + (0x10D6, "\u{1c96}"), + (0x10D7, "\u{1c97}"), + (0x10D8, "\u{1c98}"), + (0x10D9, "\u{1c99}"), + (0x10DA, "\u{1c9a}"), + (0x10DB, "\u{1c9b}"), + (0x10DC, "\u{1c9c}"), + (0x10DD, "\u{1c9d}"), + (0x10DE, "\u{1c9e}"), + (0x10DF, "\u{1c9f}"), + (0x10E0, "\u{1ca0}"), + (0x10E1, "\u{1ca1}"), + (0x10E2, "\u{1ca2}"), + (0x10E3, "\u{1ca3}"), + (0x10E4, "\u{1ca4}"), + (0x10E5, "\u{1ca5}"), + (0x10E6, "\u{1ca6}"), + (0x10E7, "\u{1ca7}"), + (0x10E8, "\u{1ca8}"), + (0x10E9, "\u{1ca9}"), + (0x10EA, "\u{1caa}"), + (0x10EB, "\u{1cab}"), + (0x10EC, "\u{1cac}"), + (0x10ED, "\u{1cad}"), + (0x10EE, "\u{1cae}"), + (0x10EF, "\u{1caf}"), + (0x10F0, "\u{1cb0}"), + (0x10F1, "\u{1cb1}"), + (0x10F2, "\u{1cb2}"), + (0x10F3, "\u{1cb3}"), + (0x10F4, "\u{1cb4}"), + (0x10F5, "\u{1cb5}"), + (0x10F6, "\u{1cb6}"), + (0x10F7, "\u{1cb7}"), + (0x10F8, "\u{1cb8}"), + (0x10F9, "\u{1cb9}"), + (0x10FA, "\u{1cba}"), + (0x10FD, "\u{1cbd}"), + (0x10FE, "\u{1cbe}"), + (0x10FF, "\u{1cbf}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{412}"), + (0x1C81, "\u{414}"), + (0x1C82, "\u{41e}"), + (0x1C83, "\u{421}"), + (0x1C84, "\u{422}"), + (0x1C85, "\u{422}"), + (0x1C86, "\u{42a}"), + (0x1C87, "\u{462}"), + (0x1C88, "\u{a64a}"), + (0x1D79, "\u{a77d}"), + (0x1D7D, "\u{2c63}"), + (0x1D8E, "\u{a7c6}"), + (0x1E01, "\u{1e00}"), + (0x1E03, "\u{1e02}"), + (0x1E05, "\u{1e04}"), + (0x1E07, "\u{1e06}"), + (0x1E09, "\u{1e08}"), + (0x1E0B, "\u{1e0a}"), + (0x1E0D, "\u{1e0c}"), + (0x1E0F, "\u{1e0e}"), + (0x1E11, "\u{1e10}"), + (0x1E13, "\u{1e12}"), + (0x1E15, "\u{1e14}"), + (0x1E17, "\u{1e16}"), + (0x1E19, "\u{1e18}"), + (0x1E1B, "\u{1e1a}"), + (0x1E1D, "\u{1e1c}"), + (0x1E1F, "\u{1e1e}"), + (0x1E21, "\u{1e20}"), + (0x1E23, "\u{1e22}"), + (0x1E25, "\u{1e24}"), + (0x1E27, "\u{1e26}"), + (0x1E29, "\u{1e28}"), + (0x1E2B, "\u{1e2a}"), + (0x1E2D, "\u{1e2c}"), + (0x1E2F, "\u{1e2e}"), + (0x1E31, "\u{1e30}"), + (0x1E33, "\u{1e32}"), + (0x1E35, "\u{1e34}"), + (0x1E37, "\u{1e36}"), + (0x1E39, "\u{1e38}"), + (0x1E3B, "\u{1e3a}"), + (0x1E3D, "\u{1e3c}"), + (0x1E3F, "\u{1e3e}"), + (0x1E41, "\u{1e40}"), + (0x1E43, "\u{1e42}"), + (0x1E45, "\u{1e44}"), + (0x1E47, "\u{1e46}"), + (0x1E49, "\u{1e48}"), + (0x1E4B, "\u{1e4a}"), + (0x1E4D, "\u{1e4c}"), + (0x1E4F, "\u{1e4e}"), + (0x1E51, "\u{1e50}"), + (0x1E53, "\u{1e52}"), + (0x1E55, "\u{1e54}"), + (0x1E57, "\u{1e56}"), + (0x1E59, "\u{1e58}"), + (0x1E5B, "\u{1e5a}"), + (0x1E5D, "\u{1e5c}"), + (0x1E5F, "\u{1e5e}"), + (0x1E61, "\u{1e60}"), + (0x1E63, "\u{1e62}"), + (0x1E65, "\u{1e64}"), + (0x1E67, "\u{1e66}"), + (0x1E69, "\u{1e68}"), + (0x1E6B, "\u{1e6a}"), + (0x1E6D, "\u{1e6c}"), + (0x1E6F, "\u{1e6e}"), + (0x1E71, "\u{1e70}"), + (0x1E73, "\u{1e72}"), + (0x1E75, "\u{1e74}"), + (0x1E77, "\u{1e76}"), + (0x1E79, "\u{1e78}"), + (0x1E7B, "\u{1e7a}"), + (0x1E7D, "\u{1e7c}"), + (0x1E7F, "\u{1e7e}"), + (0x1E81, "\u{1e80}"), + (0x1E83, "\u{1e82}"), + (0x1E85, "\u{1e84}"), + (0x1E87, "\u{1e86}"), + (0x1E89, "\u{1e88}"), + (0x1E8B, "\u{1e8a}"), + (0x1E8D, "\u{1e8c}"), + (0x1E8F, "\u{1e8e}"), + (0x1E91, "\u{1e90}"), + (0x1E93, "\u{1e92}"), + (0x1E95, "\u{1e94}"), + (0x1E96, "\u{48}\u{331}"), + (0x1E97, "\u{54}\u{308}"), + (0x1E98, "\u{57}\u{30a}"), + (0x1E99, "\u{59}\u{30a}"), + (0x1E9A, "\u{41}\u{2be}"), + (0x1E9B, "\u{1e60}"), + (0x1EA1, "\u{1ea0}"), + (0x1EA3, "\u{1ea2}"), + (0x1EA5, "\u{1ea4}"), + (0x1EA7, "\u{1ea6}"), + (0x1EA9, "\u{1ea8}"), + (0x1EAB, "\u{1eaa}"), + (0x1EAD, "\u{1eac}"), + (0x1EAF, "\u{1eae}"), + (0x1EB1, "\u{1eb0}"), + (0x1EB3, "\u{1eb2}"), + (0x1EB5, "\u{1eb4}"), + (0x1EB7, "\u{1eb6}"), + (0x1EB9, "\u{1eb8}"), + (0x1EBB, "\u{1eba}"), + (0x1EBD, "\u{1ebc}"), + (0x1EBF, "\u{1ebe}"), + (0x1EC1, "\u{1ec0}"), + (0x1EC3, "\u{1ec2}"), + (0x1EC5, "\u{1ec4}"), + (0x1EC7, "\u{1ec6}"), + (0x1EC9, "\u{1ec8}"), + (0x1ECB, "\u{1eca}"), + (0x1ECD, "\u{1ecc}"), + (0x1ECF, "\u{1ece}"), + (0x1ED1, "\u{1ed0}"), + (0x1ED3, "\u{1ed2}"), + (0x1ED5, "\u{1ed4}"), + (0x1ED7, "\u{1ed6}"), + (0x1ED9, "\u{1ed8}"), + (0x1EDB, "\u{1eda}"), + (0x1EDD, "\u{1edc}"), + (0x1EDF, "\u{1ede}"), + (0x1EE1, "\u{1ee0}"), + (0x1EE3, "\u{1ee2}"), + (0x1EE5, "\u{1ee4}"), + (0x1EE7, "\u{1ee6}"), + (0x1EE9, "\u{1ee8}"), + (0x1EEB, "\u{1eea}"), + (0x1EED, "\u{1eec}"), + (0x1EEF, "\u{1eee}"), + (0x1EF1, "\u{1ef0}"), + (0x1EF3, "\u{1ef2}"), + (0x1EF5, "\u{1ef4}"), + (0x1EF7, "\u{1ef6}"), + (0x1EF9, "\u{1ef8}"), + (0x1EFB, "\u{1efa}"), + (0x1EFD, "\u{1efc}"), + (0x1EFF, "\u{1efe}"), + (0x1F00, "\u{1f08}"), + (0x1F01, "\u{1f09}"), + (0x1F02, "\u{1f0a}"), + (0x1F03, "\u{1f0b}"), + (0x1F04, "\u{1f0c}"), + (0x1F05, "\u{1f0d}"), + (0x1F06, "\u{1f0e}"), + (0x1F07, "\u{1f0f}"), + (0x1F10, "\u{1f18}"), + (0x1F11, "\u{1f19}"), + (0x1F12, "\u{1f1a}"), + (0x1F13, "\u{1f1b}"), + (0x1F14, "\u{1f1c}"), + (0x1F15, "\u{1f1d}"), + (0x1F20, "\u{1f28}"), + (0x1F21, "\u{1f29}"), + (0x1F22, "\u{1f2a}"), + (0x1F23, "\u{1f2b}"), + (0x1F24, "\u{1f2c}"), + (0x1F25, "\u{1f2d}"), + (0x1F26, "\u{1f2e}"), + (0x1F27, "\u{1f2f}"), + (0x1F30, "\u{1f38}"), + (0x1F31, "\u{1f39}"), + (0x1F32, "\u{1f3a}"), + (0x1F33, "\u{1f3b}"), + (0x1F34, "\u{1f3c}"), + (0x1F35, "\u{1f3d}"), + (0x1F36, "\u{1f3e}"), + (0x1F37, "\u{1f3f}"), + (0x1F40, "\u{1f48}"), + (0x1F41, "\u{1f49}"), + (0x1F42, "\u{1f4a}"), + (0x1F43, "\u{1f4b}"), + (0x1F44, "\u{1f4c}"), + (0x1F45, "\u{1f4d}"), + (0x1F50, "\u{3a5}\u{313}"), + (0x1F51, "\u{1f59}"), + (0x1F52, "\u{3a5}\u{313}\u{300}"), + (0x1F53, "\u{1f5b}"), + (0x1F54, "\u{3a5}\u{313}\u{301}"), + (0x1F55, "\u{1f5d}"), + (0x1F56, "\u{3a5}\u{313}\u{342}"), + (0x1F57, "\u{1f5f}"), + (0x1F60, "\u{1f68}"), + (0x1F61, "\u{1f69}"), + (0x1F62, "\u{1f6a}"), + (0x1F63, "\u{1f6b}"), + (0x1F64, "\u{1f6c}"), + (0x1F65, "\u{1f6d}"), + (0x1F66, "\u{1f6e}"), + (0x1F67, "\u{1f6f}"), + (0x1F70, "\u{1fba}"), + (0x1F71, "\u{1fbb}"), + (0x1F72, "\u{1fc8}"), + (0x1F73, "\u{1fc9}"), + (0x1F74, "\u{1fca}"), + (0x1F75, "\u{1fcb}"), + (0x1F76, "\u{1fda}"), + (0x1F77, "\u{1fdb}"), + (0x1F78, "\u{1ff8}"), + (0x1F79, "\u{1ff9}"), + (0x1F7A, "\u{1fea}"), + (0x1F7B, "\u{1feb}"), + (0x1F7C, "\u{1ffa}"), + (0x1F7D, "\u{1ffb}"), + (0x1F80, "\u{1f08}\u{399}"), + (0x1F81, "\u{1f09}\u{399}"), + (0x1F82, "\u{1f0a}\u{399}"), + (0x1F83, "\u{1f0b}\u{399}"), + (0x1F84, "\u{1f0c}\u{399}"), + (0x1F85, "\u{1f0d}\u{399}"), + (0x1F86, "\u{1f0e}\u{399}"), + (0x1F87, "\u{1f0f}\u{399}"), + (0x1F88, "\u{1f08}\u{399}"), + (0x1F89, "\u{1f09}\u{399}"), + (0x1F8A, "\u{1f0a}\u{399}"), + (0x1F8B, "\u{1f0b}\u{399}"), + (0x1F8C, "\u{1f0c}\u{399}"), + (0x1F8D, "\u{1f0d}\u{399}"), + (0x1F8E, "\u{1f0e}\u{399}"), + (0x1F8F, "\u{1f0f}\u{399}"), + (0x1F90, "\u{1f28}\u{399}"), + (0x1F91, "\u{1f29}\u{399}"), + (0x1F92, "\u{1f2a}\u{399}"), + (0x1F93, "\u{1f2b}\u{399}"), + (0x1F94, "\u{1f2c}\u{399}"), + (0x1F95, "\u{1f2d}\u{399}"), + (0x1F96, "\u{1f2e}\u{399}"), + (0x1F97, "\u{1f2f}\u{399}"), + (0x1F98, "\u{1f28}\u{399}"), + (0x1F99, "\u{1f29}\u{399}"), + (0x1F9A, "\u{1f2a}\u{399}"), + (0x1F9B, "\u{1f2b}\u{399}"), + (0x1F9C, "\u{1f2c}\u{399}"), + (0x1F9D, "\u{1f2d}\u{399}"), + (0x1F9E, "\u{1f2e}\u{399}"), + (0x1F9F, "\u{1f2f}\u{399}"), + (0x1FA0, "\u{1f68}\u{399}"), + (0x1FA1, "\u{1f69}\u{399}"), + (0x1FA2, "\u{1f6a}\u{399}"), + (0x1FA3, "\u{1f6b}\u{399}"), + (0x1FA4, "\u{1f6c}\u{399}"), + (0x1FA5, "\u{1f6d}\u{399}"), + (0x1FA6, "\u{1f6e}\u{399}"), + (0x1FA7, "\u{1f6f}\u{399}"), + (0x1FA8, "\u{1f68}\u{399}"), + (0x1FA9, "\u{1f69}\u{399}"), + (0x1FAA, "\u{1f6a}\u{399}"), + (0x1FAB, "\u{1f6b}\u{399}"), + (0x1FAC, "\u{1f6c}\u{399}"), + (0x1FAD, "\u{1f6d}\u{399}"), + (0x1FAE, "\u{1f6e}\u{399}"), + (0x1FAF, "\u{1f6f}\u{399}"), + (0x1FB0, "\u{1fb8}"), + (0x1FB1, "\u{1fb9}"), + (0x1FB2, "\u{1fba}\u{399}"), + (0x1FB3, "\u{391}\u{399}"), + (0x1FB4, "\u{386}\u{399}"), + (0x1FB6, "\u{391}\u{342}"), + (0x1FB7, "\u{391}\u{342}\u{399}"), + (0x1FBC, "\u{391}\u{399}"), + (0x1FBE, "\u{399}"), + (0x1FC2, "\u{1fca}\u{399}"), + (0x1FC3, "\u{397}\u{399}"), + (0x1FC4, "\u{389}\u{399}"), + (0x1FC6, "\u{397}\u{342}"), + (0x1FC7, "\u{397}\u{342}\u{399}"), + (0x1FCC, "\u{397}\u{399}"), + (0x1FD0, "\u{1fd8}"), + (0x1FD1, "\u{1fd9}"), + (0x1FD2, "\u{399}\u{308}\u{300}"), + (0x1FD3, "\u{399}\u{308}\u{301}"), + (0x1FD6, "\u{399}\u{342}"), + (0x1FD7, "\u{399}\u{308}\u{342}"), + (0x1FE0, "\u{1fe8}"), + (0x1FE1, "\u{1fe9}"), + (0x1FE2, "\u{3a5}\u{308}\u{300}"), + (0x1FE3, "\u{3a5}\u{308}\u{301}"), + (0x1FE4, "\u{3a1}\u{313}"), + (0x1FE5, "\u{1fec}"), + (0x1FE6, "\u{3a5}\u{342}"), + (0x1FE7, "\u{3a5}\u{308}\u{342}"), + (0x1FF2, "\u{1ffa}\u{399}"), + (0x1FF3, "\u{3a9}\u{399}"), + (0x1FF4, "\u{38f}\u{399}"), + (0x1FF6, "\u{3a9}\u{342}"), + (0x1FF7, "\u{3a9}\u{342}\u{399}"), + (0x1FFC, "\u{3a9}\u{399}"), + (0x214E, "\u{2132}"), + (0x2170, "\u{2160}"), + (0x2171, "\u{2161}"), + (0x2172, "\u{2162}"), + (0x2173, "\u{2163}"), + (0x2174, "\u{2164}"), + (0x2175, "\u{2165}"), + (0x2176, "\u{2166}"), + (0x2177, "\u{2167}"), + (0x2178, "\u{2168}"), + (0x2179, "\u{2169}"), + (0x217A, "\u{216a}"), + (0x217B, "\u{216b}"), + (0x217C, "\u{216c}"), + (0x217D, "\u{216d}"), + (0x217E, "\u{216e}"), + (0x217F, "\u{216f}"), + (0x2184, "\u{2183}"), + (0x24D0, "\u{24b6}"), + (0x24D1, "\u{24b7}"), + (0x24D2, "\u{24b8}"), + (0x24D3, "\u{24b9}"), + (0x24D4, "\u{24ba}"), + (0x24D5, "\u{24bb}"), + (0x24D6, "\u{24bc}"), + (0x24D7, "\u{24bd}"), + (0x24D8, "\u{24be}"), + (0x24D9, "\u{24bf}"), + (0x24DA, "\u{24c0}"), + (0x24DB, "\u{24c1}"), + (0x24DC, "\u{24c2}"), + (0x24DD, "\u{24c3}"), + (0x24DE, "\u{24c4}"), + (0x24DF, "\u{24c5}"), + (0x24E0, "\u{24c6}"), + (0x24E1, "\u{24c7}"), + (0x24E2, "\u{24c8}"), + (0x24E3, "\u{24c9}"), + (0x24E4, "\u{24ca}"), + (0x24E5, "\u{24cb}"), + (0x24E6, "\u{24cc}"), + (0x24E7, "\u{24cd}"), + (0x24E8, "\u{24ce}"), + (0x24E9, "\u{24cf}"), + (0x2C30, "\u{2c00}"), + (0x2C31, "\u{2c01}"), + (0x2C32, "\u{2c02}"), + (0x2C33, "\u{2c03}"), + (0x2C34, "\u{2c04}"), + (0x2C35, "\u{2c05}"), + (0x2C36, "\u{2c06}"), + (0x2C37, "\u{2c07}"), + (0x2C38, "\u{2c08}"), + (0x2C39, "\u{2c09}"), + (0x2C3A, "\u{2c0a}"), + (0x2C3B, "\u{2c0b}"), + (0x2C3C, "\u{2c0c}"), + (0x2C3D, "\u{2c0d}"), + (0x2C3E, "\u{2c0e}"), + (0x2C3F, "\u{2c0f}"), + (0x2C40, "\u{2c10}"), + (0x2C41, "\u{2c11}"), + (0x2C42, "\u{2c12}"), + (0x2C43, "\u{2c13}"), + (0x2C44, "\u{2c14}"), + (0x2C45, "\u{2c15}"), + (0x2C46, "\u{2c16}"), + (0x2C47, "\u{2c17}"), + (0x2C48, "\u{2c18}"), + (0x2C49, "\u{2c19}"), + (0x2C4A, "\u{2c1a}"), + (0x2C4B, "\u{2c1b}"), + (0x2C4C, "\u{2c1c}"), + (0x2C4D, "\u{2c1d}"), + (0x2C4E, "\u{2c1e}"), + (0x2C4F, "\u{2c1f}"), + (0x2C50, "\u{2c20}"), + (0x2C51, "\u{2c21}"), + (0x2C52, "\u{2c22}"), + (0x2C53, "\u{2c23}"), + (0x2C54, "\u{2c24}"), + (0x2C55, "\u{2c25}"), + (0x2C56, "\u{2c26}"), + (0x2C57, "\u{2c27}"), + (0x2C58, "\u{2c28}"), + (0x2C59, "\u{2c29}"), + (0x2C5A, "\u{2c2a}"), + (0x2C5B, "\u{2c2b}"), + (0x2C5C, "\u{2c2c}"), + (0x2C5D, "\u{2c2d}"), + (0x2C5E, "\u{2c2e}"), + (0x2C5F, "\u{2c2f}"), + (0x2C61, "\u{2c60}"), + (0x2C65, "\u{23a}"), + (0x2C66, "\u{23e}"), + (0x2C68, "\u{2c67}"), + (0x2C6A, "\u{2c69}"), + (0x2C6C, "\u{2c6b}"), + (0x2C73, "\u{2c72}"), + (0x2C76, "\u{2c75}"), + (0x2C81, "\u{2c80}"), + (0x2C83, "\u{2c82}"), + (0x2C85, "\u{2c84}"), + (0x2C87, "\u{2c86}"), + (0x2C89, "\u{2c88}"), + (0x2C8B, "\u{2c8a}"), + (0x2C8D, "\u{2c8c}"), + (0x2C8F, "\u{2c8e}"), + (0x2C91, "\u{2c90}"), + (0x2C93, "\u{2c92}"), + (0x2C95, "\u{2c94}"), + (0x2C97, "\u{2c96}"), + (0x2C99, "\u{2c98}"), + (0x2C9B, "\u{2c9a}"), + (0x2C9D, "\u{2c9c}"), + (0x2C9F, "\u{2c9e}"), + (0x2CA1, "\u{2ca0}"), + (0x2CA3, "\u{2ca2}"), + (0x2CA5, "\u{2ca4}"), + (0x2CA7, "\u{2ca6}"), + (0x2CA9, "\u{2ca8}"), + (0x2CAB, "\u{2caa}"), + (0x2CAD, "\u{2cac}"), + (0x2CAF, "\u{2cae}"), + (0x2CB1, "\u{2cb0}"), + (0x2CB3, "\u{2cb2}"), + (0x2CB5, "\u{2cb4}"), + (0x2CB7, "\u{2cb6}"), + (0x2CB9, "\u{2cb8}"), + (0x2CBB, "\u{2cba}"), + (0x2CBD, "\u{2cbc}"), + (0x2CBF, "\u{2cbe}"), + (0x2CC1, "\u{2cc0}"), + (0x2CC3, "\u{2cc2}"), + (0x2CC5, "\u{2cc4}"), + (0x2CC7, "\u{2cc6}"), + (0x2CC9, "\u{2cc8}"), + (0x2CCB, "\u{2cca}"), + (0x2CCD, "\u{2ccc}"), + (0x2CCF, "\u{2cce}"), + (0x2CD1, "\u{2cd0}"), + (0x2CD3, "\u{2cd2}"), + (0x2CD5, "\u{2cd4}"), + (0x2CD7, "\u{2cd6}"), + (0x2CD9, "\u{2cd8}"), + (0x2CDB, "\u{2cda}"), + (0x2CDD, "\u{2cdc}"), + (0x2CDF, "\u{2cde}"), + (0x2CE1, "\u{2ce0}"), + (0x2CE3, "\u{2ce2}"), + (0x2CEC, "\u{2ceb}"), + (0x2CEE, "\u{2ced}"), + (0x2CF3, "\u{2cf2}"), + (0x2D00, "\u{10a0}"), + (0x2D01, "\u{10a1}"), + (0x2D02, "\u{10a2}"), + (0x2D03, "\u{10a3}"), + (0x2D04, "\u{10a4}"), + (0x2D05, "\u{10a5}"), + (0x2D06, "\u{10a6}"), + (0x2D07, "\u{10a7}"), + (0x2D08, "\u{10a8}"), + (0x2D09, "\u{10a9}"), + (0x2D0A, "\u{10aa}"), + (0x2D0B, "\u{10ab}"), + (0x2D0C, "\u{10ac}"), + (0x2D0D, "\u{10ad}"), + (0x2D0E, "\u{10ae}"), + (0x2D0F, "\u{10af}"), + (0x2D10, "\u{10b0}"), + (0x2D11, "\u{10b1}"), + (0x2D12, "\u{10b2}"), + (0x2D13, "\u{10b3}"), + (0x2D14, "\u{10b4}"), + (0x2D15, "\u{10b5}"), + (0x2D16, "\u{10b6}"), + (0x2D17, "\u{10b7}"), + (0x2D18, "\u{10b8}"), + (0x2D19, "\u{10b9}"), + (0x2D1A, "\u{10ba}"), + (0x2D1B, "\u{10bb}"), + (0x2D1C, "\u{10bc}"), + (0x2D1D, "\u{10bd}"), + (0x2D1E, "\u{10be}"), + (0x2D1F, "\u{10bf}"), + (0x2D20, "\u{10c0}"), + (0x2D21, "\u{10c1}"), + (0x2D22, "\u{10c2}"), + (0x2D23, "\u{10c3}"), + (0x2D24, "\u{10c4}"), + (0x2D25, "\u{10c5}"), + (0x2D27, "\u{10c7}"), + (0x2D2D, "\u{10cd}"), + (0xA641, "\u{a640}"), + (0xA643, "\u{a642}"), + (0xA645, "\u{a644}"), + (0xA647, "\u{a646}"), + (0xA649, "\u{a648}"), + (0xA64B, "\u{a64a}"), + (0xA64D, "\u{a64c}"), + (0xA64F, "\u{a64e}"), + (0xA651, "\u{a650}"), + (0xA653, "\u{a652}"), + (0xA655, "\u{a654}"), + (0xA657, "\u{a656}"), + (0xA659, "\u{a658}"), + (0xA65B, "\u{a65a}"), + (0xA65D, "\u{a65c}"), + (0xA65F, "\u{a65e}"), + (0xA661, "\u{a660}"), + (0xA663, "\u{a662}"), + (0xA665, "\u{a664}"), + (0xA667, "\u{a666}"), + (0xA669, "\u{a668}"), + (0xA66B, "\u{a66a}"), + (0xA66D, "\u{a66c}"), + (0xA681, "\u{a680}"), + (0xA683, "\u{a682}"), + (0xA685, "\u{a684}"), + (0xA687, "\u{a686}"), + (0xA689, "\u{a688}"), + (0xA68B, "\u{a68a}"), + (0xA68D, "\u{a68c}"), + (0xA68F, "\u{a68e}"), + (0xA691, "\u{a690}"), + (0xA693, "\u{a692}"), + (0xA695, "\u{a694}"), + (0xA697, "\u{a696}"), + (0xA699, "\u{a698}"), + (0xA69B, "\u{a69a}"), + (0xA723, "\u{a722}"), + (0xA725, "\u{a724}"), + (0xA727, "\u{a726}"), + (0xA729, "\u{a728}"), + (0xA72B, "\u{a72a}"), + (0xA72D, "\u{a72c}"), + (0xA72F, "\u{a72e}"), + (0xA733, "\u{a732}"), + (0xA735, "\u{a734}"), + (0xA737, "\u{a736}"), + (0xA739, "\u{a738}"), + (0xA73B, "\u{a73a}"), + (0xA73D, "\u{a73c}"), + (0xA73F, "\u{a73e}"), + (0xA741, "\u{a740}"), + (0xA743, "\u{a742}"), + (0xA745, "\u{a744}"), + (0xA747, "\u{a746}"), + (0xA749, "\u{a748}"), + (0xA74B, "\u{a74a}"), + (0xA74D, "\u{a74c}"), + (0xA74F, "\u{a74e}"), + (0xA751, "\u{a750}"), + (0xA753, "\u{a752}"), + (0xA755, "\u{a754}"), + (0xA757, "\u{a756}"), + (0xA759, "\u{a758}"), + (0xA75B, "\u{a75a}"), + (0xA75D, "\u{a75c}"), + (0xA75F, "\u{a75e}"), + (0xA761, "\u{a760}"), + (0xA763, "\u{a762}"), + (0xA765, "\u{a764}"), + (0xA767, "\u{a766}"), + (0xA769, "\u{a768}"), + (0xA76B, "\u{a76a}"), + (0xA76D, "\u{a76c}"), + (0xA76F, "\u{a76e}"), + (0xA77A, "\u{a779}"), + (0xA77C, "\u{a77b}"), + (0xA77F, "\u{a77e}"), + (0xA781, "\u{a780}"), + (0xA783, "\u{a782}"), + (0xA785, "\u{a784}"), + (0xA787, "\u{a786}"), + (0xA78C, "\u{a78b}"), + (0xA791, "\u{a790}"), + (0xA793, "\u{a792}"), + (0xA794, "\u{a7c4}"), + (0xA797, "\u{a796}"), + (0xA799, "\u{a798}"), + (0xA79B, "\u{a79a}"), + (0xA79D, "\u{a79c}"), + (0xA79F, "\u{a79e}"), + (0xA7A1, "\u{a7a0}"), + (0xA7A3, "\u{a7a2}"), + (0xA7A5, "\u{a7a4}"), + (0xA7A7, "\u{a7a6}"), + (0xA7A9, "\u{a7a8}"), + (0xA7B5, "\u{a7b4}"), + (0xA7B7, "\u{a7b6}"), + (0xA7B9, "\u{a7b8}"), + (0xA7BB, "\u{a7ba}"), + (0xA7BD, "\u{a7bc}"), + (0xA7BF, "\u{a7be}"), + (0xA7C1, "\u{a7c0}"), + (0xA7C3, "\u{a7c2}"), + (0xA7C8, "\u{a7c7}"), + (0xA7CA, "\u{a7c9}"), + (0xA7D1, "\u{a7d0}"), + (0xA7D7, "\u{a7d6}"), + (0xA7D9, "\u{a7d8}"), + (0xA7F6, "\u{a7f5}"), + (0xAB53, "\u{a7b3}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{46}\u{46}"), + (0xFB01, "\u{46}\u{49}"), + (0xFB02, "\u{46}\u{4c}"), + (0xFB03, "\u{46}\u{46}\u{49}"), + (0xFB04, "\u{46}\u{46}\u{4c}"), + (0xFB05, "\u{53}\u{54}"), + (0xFB06, "\u{53}\u{54}"), + (0xFB13, "\u{544}\u{546}"), + (0xFB14, "\u{544}\u{535}"), + (0xFB15, "\u{544}\u{53b}"), + (0xFB16, "\u{54e}\u{546}"), + (0xFB17, "\u{544}\u{53d}"), + (0xFF41, "\u{ff21}"), + (0xFF42, "\u{ff22}"), + (0xFF43, "\u{ff23}"), + (0xFF44, "\u{ff24}"), + (0xFF45, "\u{ff25}"), + (0xFF46, "\u{ff26}"), + (0xFF47, "\u{ff27}"), + (0xFF48, "\u{ff28}"), + (0xFF49, "\u{ff29}"), + (0xFF4A, "\u{ff2a}"), + (0xFF4B, "\u{ff2b}"), + (0xFF4C, "\u{ff2c}"), + (0xFF4D, "\u{ff2d}"), + (0xFF4E, "\u{ff2e}"), + (0xFF4F, "\u{ff2f}"), + (0xFF50, "\u{ff30}"), + (0xFF51, "\u{ff31}"), + (0xFF52, "\u{ff32}"), + (0xFF53, "\u{ff33}"), + (0xFF54, "\u{ff34}"), + (0xFF55, "\u{ff35}"), + (0xFF56, "\u{ff36}"), + (0xFF57, "\u{ff37}"), + (0xFF58, "\u{ff38}"), + (0xFF59, "\u{ff39}"), + (0xFF5A, "\u{ff3a}"), + (0x10428, "\u{10400}"), + (0x10429, "\u{10401}"), + (0x1042A, "\u{10402}"), + (0x1042B, "\u{10403}"), + (0x1042C, "\u{10404}"), + (0x1042D, "\u{10405}"), + (0x1042E, "\u{10406}"), + (0x1042F, "\u{10407}"), + (0x10430, "\u{10408}"), + (0x10431, "\u{10409}"), + (0x10432, "\u{1040a}"), + (0x10433, "\u{1040b}"), + (0x10434, "\u{1040c}"), + (0x10435, "\u{1040d}"), + (0x10436, "\u{1040e}"), + (0x10437, "\u{1040f}"), + (0x10438, "\u{10410}"), + (0x10439, "\u{10411}"), + (0x1043A, "\u{10412}"), + (0x1043B, "\u{10413}"), + (0x1043C, "\u{10414}"), + (0x1043D, "\u{10415}"), + (0x1043E, "\u{10416}"), + (0x1043F, "\u{10417}"), + (0x10440, "\u{10418}"), + (0x10441, "\u{10419}"), + (0x10442, "\u{1041a}"), + (0x10443, "\u{1041b}"), + (0x10444, "\u{1041c}"), + (0x10445, "\u{1041d}"), + (0x10446, "\u{1041e}"), + (0x10447, "\u{1041f}"), + (0x10448, "\u{10420}"), + (0x10449, "\u{10421}"), + (0x1044A, "\u{10422}"), + (0x1044B, "\u{10423}"), + (0x1044C, "\u{10424}"), + (0x1044D, "\u{10425}"), + (0x1044E, "\u{10426}"), + (0x1044F, "\u{10427}"), + (0x104D8, "\u{104b0}"), + (0x104D9, "\u{104b1}"), + (0x104DA, "\u{104b2}"), + (0x104DB, "\u{104b3}"), + (0x104DC, "\u{104b4}"), + (0x104DD, "\u{104b5}"), + (0x104DE, "\u{104b6}"), + (0x104DF, "\u{104b7}"), + (0x104E0, "\u{104b8}"), + (0x104E1, "\u{104b9}"), + (0x104E2, "\u{104ba}"), + (0x104E3, "\u{104bb}"), + (0x104E4, "\u{104bc}"), + (0x104E5, "\u{104bd}"), + (0x104E6, "\u{104be}"), + (0x104E7, "\u{104bf}"), + (0x104E8, "\u{104c0}"), + (0x104E9, "\u{104c1}"), + (0x104EA, "\u{104c2}"), + (0x104EB, "\u{104c3}"), + (0x104EC, "\u{104c4}"), + (0x104ED, "\u{104c5}"), + (0x104EE, "\u{104c6}"), + (0x104EF, "\u{104c7}"), + (0x104F0, "\u{104c8}"), + (0x104F1, "\u{104c9}"), + (0x104F2, "\u{104ca}"), + (0x104F3, "\u{104cb}"), + (0x104F4, "\u{104cc}"), + (0x104F5, "\u{104cd}"), + (0x104F6, "\u{104ce}"), + (0x104F7, "\u{104cf}"), + (0x104F8, "\u{104d0}"), + (0x104F9, "\u{104d1}"), + (0x104FA, "\u{104d2}"), + (0x104FB, "\u{104d3}"), + (0x10597, "\u{10570}"), + (0x10598, "\u{10571}"), + (0x10599, "\u{10572}"), + (0x1059A, "\u{10573}"), + (0x1059B, "\u{10574}"), + (0x1059C, "\u{10575}"), + (0x1059D, "\u{10576}"), + (0x1059E, "\u{10577}"), + (0x1059F, "\u{10578}"), + (0x105A0, "\u{10579}"), + (0x105A1, "\u{1057a}"), + (0x105A3, "\u{1057c}"), + (0x105A4, "\u{1057d}"), + (0x105A5, "\u{1057e}"), + (0x105A6, "\u{1057f}"), + (0x105A7, "\u{10580}"), + (0x105A8, "\u{10581}"), + (0x105A9, "\u{10582}"), + (0x105AA, "\u{10583}"), + (0x105AB, "\u{10584}"), + (0x105AC, "\u{10585}"), + (0x105AD, "\u{10586}"), + (0x105AE, "\u{10587}"), + (0x105AF, "\u{10588}"), + (0x105B0, "\u{10589}"), + (0x105B1, "\u{1058a}"), + (0x105B3, "\u{1058c}"), + (0x105B4, "\u{1058d}"), + (0x105B5, "\u{1058e}"), + (0x105B6, "\u{1058f}"), + (0x105B7, "\u{10590}"), + (0x105B8, "\u{10591}"), + (0x105B9, "\u{10592}"), + (0x105BB, "\u{10594}"), + (0x105BC, "\u{10595}"), + (0x10CC0, "\u{10c80}"), + (0x10CC1, "\u{10c81}"), + (0x10CC2, "\u{10c82}"), + (0x10CC3, "\u{10c83}"), + (0x10CC4, "\u{10c84}"), + (0x10CC5, "\u{10c85}"), + (0x10CC6, "\u{10c86}"), + (0x10CC7, "\u{10c87}"), + (0x10CC8, "\u{10c88}"), + (0x10CC9, "\u{10c89}"), + (0x10CCA, "\u{10c8a}"), + (0x10CCB, "\u{10c8b}"), + (0x10CCC, "\u{10c8c}"), + (0x10CCD, "\u{10c8d}"), + (0x10CCE, "\u{10c8e}"), + (0x10CCF, "\u{10c8f}"), + (0x10CD0, "\u{10c90}"), + (0x10CD1, "\u{10c91}"), + (0x10CD2, "\u{10c92}"), + (0x10CD3, "\u{10c93}"), + (0x10CD4, "\u{10c94}"), + (0x10CD5, "\u{10c95}"), + (0x10CD6, "\u{10c96}"), + (0x10CD7, "\u{10c97}"), + (0x10CD8, "\u{10c98}"), + (0x10CD9, "\u{10c99}"), + (0x10CDA, "\u{10c9a}"), + (0x10CDB, "\u{10c9b}"), + (0x10CDC, "\u{10c9c}"), + (0x10CDD, "\u{10c9d}"), + (0x10CDE, "\u{10c9e}"), + (0x10CDF, "\u{10c9f}"), + (0x10CE0, "\u{10ca0}"), + (0x10CE1, "\u{10ca1}"), + (0x10CE2, "\u{10ca2}"), + (0x10CE3, "\u{10ca3}"), + (0x10CE4, "\u{10ca4}"), + (0x10CE5, "\u{10ca5}"), + (0x10CE6, "\u{10ca6}"), + (0x10CE7, "\u{10ca7}"), + (0x10CE8, "\u{10ca8}"), + (0x10CE9, "\u{10ca9}"), + (0x10CEA, "\u{10caa}"), + (0x10CEB, "\u{10cab}"), + (0x10CEC, "\u{10cac}"), + (0x10CED, "\u{10cad}"), + (0x10CEE, "\u{10cae}"), + (0x10CEF, "\u{10caf}"), + (0x10CF0, "\u{10cb0}"), + (0x10CF1, "\u{10cb1}"), + (0x10CF2, "\u{10cb2}"), + (0x118C0, "\u{118a0}"), + (0x118C1, "\u{118a1}"), + (0x118C2, "\u{118a2}"), + (0x118C3, "\u{118a3}"), + (0x118C4, "\u{118a4}"), + (0x118C5, "\u{118a5}"), + (0x118C6, "\u{118a6}"), + (0x118C7, "\u{118a7}"), + (0x118C8, "\u{118a8}"), + (0x118C9, "\u{118a9}"), + (0x118CA, "\u{118aa}"), + (0x118CB, "\u{118ab}"), + (0x118CC, "\u{118ac}"), + (0x118CD, "\u{118ad}"), + (0x118CE, "\u{118ae}"), + (0x118CF, "\u{118af}"), + (0x118D0, "\u{118b0}"), + (0x118D1, "\u{118b1}"), + (0x118D2, "\u{118b2}"), + (0x118D3, "\u{118b3}"), + (0x118D4, "\u{118b4}"), + (0x118D5, "\u{118b5}"), + (0x118D6, "\u{118b6}"), + (0x118D7, "\u{118b7}"), + (0x118D8, "\u{118b8}"), + (0x118D9, "\u{118b9}"), + (0x118DA, "\u{118ba}"), + (0x118DB, "\u{118bb}"), + (0x118DC, "\u{118bc}"), + (0x118DD, "\u{118bd}"), + (0x118DE, "\u{118be}"), + (0x118DF, "\u{118bf}"), + (0x16E60, "\u{16e40}"), + (0x16E61, "\u{16e41}"), + (0x16E62, "\u{16e42}"), + (0x16E63, "\u{16e43}"), + (0x16E64, "\u{16e44}"), + (0x16E65, "\u{16e45}"), + (0x16E66, "\u{16e46}"), + (0x16E67, "\u{16e47}"), + (0x16E68, "\u{16e48}"), + (0x16E69, "\u{16e49}"), + (0x16E6A, "\u{16e4a}"), + (0x16E6B, "\u{16e4b}"), + (0x16E6C, "\u{16e4c}"), + (0x16E6D, "\u{16e4d}"), + (0x16E6E, "\u{16e4e}"), + (0x16E6F, "\u{16e4f}"), + (0x16E70, "\u{16e50}"), + (0x16E71, "\u{16e51}"), + (0x16E72, "\u{16e52}"), + (0x16E73, "\u{16e53}"), + (0x16E74, "\u{16e54}"), + (0x16E75, "\u{16e55}"), + (0x16E76, "\u{16e56}"), + (0x16E77, "\u{16e57}"), + (0x16E78, "\u{16e58}"), + (0x16E79, "\u{16e59}"), + (0x16E7A, "\u{16e5a}"), + (0x16E7B, "\u{16e5b}"), + (0x16E7C, "\u{16e5c}"), + (0x16E7D, "\u{16e5d}"), + (0x16E7E, "\u{16e5e}"), + (0x16E7F, "\u{16e5f}"), + (0x1E922, "\u{1e900}"), + (0x1E923, "\u{1e901}"), + (0x1E924, "\u{1e902}"), + (0x1E925, "\u{1e903}"), + (0x1E926, "\u{1e904}"), + (0x1E927, "\u{1e905}"), + (0x1E928, "\u{1e906}"), + (0x1E929, "\u{1e907}"), + (0x1E92A, "\u{1e908}"), + (0x1E92B, "\u{1e909}"), + (0x1E92C, "\u{1e90a}"), + (0x1E92D, "\u{1e90b}"), + (0x1E92E, "\u{1e90c}"), + (0x1E92F, "\u{1e90d}"), + (0x1E930, "\u{1e90e}"), + (0x1E931, "\u{1e90f}"), + (0x1E932, "\u{1e910}"), + (0x1E933, "\u{1e911}"), + (0x1E934, "\u{1e912}"), + (0x1E935, "\u{1e913}"), + (0x1E936, "\u{1e914}"), + (0x1E937, "\u{1e915}"), + (0x1E938, "\u{1e916}"), + (0x1E939, "\u{1e917}"), + (0x1E93A, "\u{1e918}"), + (0x1E93B, "\u{1e919}"), + (0x1E93C, "\u{1e91a}"), + (0x1E93D, "\u{1e91b}"), + (0x1E93E, "\u{1e91c}"), + (0x1E93F, "\u{1e91d}"), + (0x1E940, "\u{1e91e}"), + (0x1E941, "\u{1e91f}"), + (0x1E942, "\u{1e920}"), + (0x1E943, "\u{1e921}"), +] diff --git a/crates/sqlbuild-core/src/text/upper_mappings/unicode_16_0_0.in b/crates/sqlbuild-core/src/text/upper_mappings/unicode_16_0_0.in new file mode 100644 index 0000000000..4375565041 --- /dev/null +++ b/crates/sqlbuild-core/src/text/upper_mappings/unicode_16_0_0.in @@ -0,0 +1,1556 @@ +// Generated by `scripts/generate_python_char_table.py`; do not edit. +// Every character `str.upper()` changes in Unicode 16.0.0, in code point order. +&[ + (0x0061, "\u{41}"), + (0x0062, "\u{42}"), + (0x0063, "\u{43}"), + (0x0064, "\u{44}"), + (0x0065, "\u{45}"), + (0x0066, "\u{46}"), + (0x0067, "\u{47}"), + (0x0068, "\u{48}"), + (0x0069, "\u{49}"), + (0x006A, "\u{4a}"), + (0x006B, "\u{4b}"), + (0x006C, "\u{4c}"), + (0x006D, "\u{4d}"), + (0x006E, "\u{4e}"), + (0x006F, "\u{4f}"), + (0x0070, "\u{50}"), + (0x0071, "\u{51}"), + (0x0072, "\u{52}"), + (0x0073, "\u{53}"), + (0x0074, "\u{54}"), + (0x0075, "\u{55}"), + (0x0076, "\u{56}"), + (0x0077, "\u{57}"), + (0x0078, "\u{58}"), + (0x0079, "\u{59}"), + (0x007A, "\u{5a}"), + (0x00B5, "\u{39c}"), + (0x00DF, "\u{53}\u{53}"), + (0x00E0, "\u{c0}"), + (0x00E1, "\u{c1}"), + (0x00E2, "\u{c2}"), + (0x00E3, "\u{c3}"), + (0x00E4, "\u{c4}"), + (0x00E5, "\u{c5}"), + (0x00E6, "\u{c6}"), + (0x00E7, "\u{c7}"), + (0x00E8, "\u{c8}"), + (0x00E9, "\u{c9}"), + (0x00EA, "\u{ca}"), + (0x00EB, "\u{cb}"), + (0x00EC, "\u{cc}"), + (0x00ED, "\u{cd}"), + (0x00EE, "\u{ce}"), + (0x00EF, "\u{cf}"), + (0x00F0, "\u{d0}"), + (0x00F1, "\u{d1}"), + (0x00F2, "\u{d2}"), + (0x00F3, "\u{d3}"), + (0x00F4, "\u{d4}"), + (0x00F5, "\u{d5}"), + (0x00F6, "\u{d6}"), + (0x00F8, "\u{d8}"), + (0x00F9, "\u{d9}"), + (0x00FA, "\u{da}"), + (0x00FB, "\u{db}"), + (0x00FC, "\u{dc}"), + (0x00FD, "\u{dd}"), + (0x00FE, "\u{de}"), + (0x00FF, "\u{178}"), + (0x0101, "\u{100}"), + (0x0103, "\u{102}"), + (0x0105, "\u{104}"), + (0x0107, "\u{106}"), + (0x0109, "\u{108}"), + (0x010B, "\u{10a}"), + (0x010D, "\u{10c}"), + (0x010F, "\u{10e}"), + (0x0111, "\u{110}"), + (0x0113, "\u{112}"), + (0x0115, "\u{114}"), + (0x0117, "\u{116}"), + (0x0119, "\u{118}"), + (0x011B, "\u{11a}"), + (0x011D, "\u{11c}"), + (0x011F, "\u{11e}"), + (0x0121, "\u{120}"), + (0x0123, "\u{122}"), + (0x0125, "\u{124}"), + (0x0127, "\u{126}"), + (0x0129, "\u{128}"), + (0x012B, "\u{12a}"), + (0x012D, "\u{12c}"), + (0x012F, "\u{12e}"), + (0x0131, "\u{49}"), + (0x0133, "\u{132}"), + (0x0135, "\u{134}"), + (0x0137, "\u{136}"), + (0x013A, "\u{139}"), + (0x013C, "\u{13b}"), + (0x013E, "\u{13d}"), + (0x0140, "\u{13f}"), + (0x0142, "\u{141}"), + (0x0144, "\u{143}"), + (0x0146, "\u{145}"), + (0x0148, "\u{147}"), + (0x0149, "\u{2bc}\u{4e}"), + (0x014B, "\u{14a}"), + (0x014D, "\u{14c}"), + (0x014F, "\u{14e}"), + (0x0151, "\u{150}"), + (0x0153, "\u{152}"), + (0x0155, "\u{154}"), + (0x0157, "\u{156}"), + (0x0159, "\u{158}"), + (0x015B, "\u{15a}"), + (0x015D, "\u{15c}"), + (0x015F, "\u{15e}"), + (0x0161, "\u{160}"), + (0x0163, "\u{162}"), + (0x0165, "\u{164}"), + (0x0167, "\u{166}"), + (0x0169, "\u{168}"), + (0x016B, "\u{16a}"), + (0x016D, "\u{16c}"), + (0x016F, "\u{16e}"), + (0x0171, "\u{170}"), + (0x0173, "\u{172}"), + (0x0175, "\u{174}"), + (0x0177, "\u{176}"), + (0x017A, "\u{179}"), + (0x017C, "\u{17b}"), + (0x017E, "\u{17d}"), + (0x017F, "\u{53}"), + (0x0180, "\u{243}"), + (0x0183, "\u{182}"), + (0x0185, "\u{184}"), + (0x0188, "\u{187}"), + (0x018C, "\u{18b}"), + (0x0192, "\u{191}"), + (0x0195, "\u{1f6}"), + (0x0199, "\u{198}"), + (0x019A, "\u{23d}"), + (0x019B, "\u{a7dc}"), + (0x019E, "\u{220}"), + (0x01A1, "\u{1a0}"), + (0x01A3, "\u{1a2}"), + (0x01A5, "\u{1a4}"), + (0x01A8, "\u{1a7}"), + (0x01AD, "\u{1ac}"), + (0x01B0, "\u{1af}"), + (0x01B4, "\u{1b3}"), + (0x01B6, "\u{1b5}"), + (0x01B9, "\u{1b8}"), + (0x01BD, "\u{1bc}"), + (0x01BF, "\u{1f7}"), + (0x01C5, "\u{1c4}"), + (0x01C6, "\u{1c4}"), + (0x01C8, "\u{1c7}"), + (0x01C9, "\u{1c7}"), + (0x01CB, "\u{1ca}"), + (0x01CC, "\u{1ca}"), + (0x01CE, "\u{1cd}"), + (0x01D0, "\u{1cf}"), + (0x01D2, "\u{1d1}"), + (0x01D4, "\u{1d3}"), + (0x01D6, "\u{1d5}"), + (0x01D8, "\u{1d7}"), + (0x01DA, "\u{1d9}"), + (0x01DC, "\u{1db}"), + (0x01DD, "\u{18e}"), + (0x01DF, "\u{1de}"), + (0x01E1, "\u{1e0}"), + (0x01E3, "\u{1e2}"), + (0x01E5, "\u{1e4}"), + (0x01E7, "\u{1e6}"), + (0x01E9, "\u{1e8}"), + (0x01EB, "\u{1ea}"), + (0x01ED, "\u{1ec}"), + (0x01EF, "\u{1ee}"), + (0x01F0, "\u{4a}\u{30c}"), + (0x01F2, "\u{1f1}"), + (0x01F3, "\u{1f1}"), + (0x01F5, "\u{1f4}"), + (0x01F9, "\u{1f8}"), + (0x01FB, "\u{1fa}"), + (0x01FD, "\u{1fc}"), + (0x01FF, "\u{1fe}"), + (0x0201, "\u{200}"), + (0x0203, "\u{202}"), + (0x0205, "\u{204}"), + (0x0207, "\u{206}"), + (0x0209, "\u{208}"), + (0x020B, "\u{20a}"), + (0x020D, "\u{20c}"), + (0x020F, "\u{20e}"), + (0x0211, "\u{210}"), + (0x0213, "\u{212}"), + (0x0215, "\u{214}"), + (0x0217, "\u{216}"), + (0x0219, "\u{218}"), + (0x021B, "\u{21a}"), + (0x021D, "\u{21c}"), + (0x021F, "\u{21e}"), + (0x0223, "\u{222}"), + (0x0225, "\u{224}"), + (0x0227, "\u{226}"), + (0x0229, "\u{228}"), + (0x022B, "\u{22a}"), + (0x022D, "\u{22c}"), + (0x022F, "\u{22e}"), + (0x0231, "\u{230}"), + (0x0233, "\u{232}"), + (0x023C, "\u{23b}"), + (0x023F, "\u{2c7e}"), + (0x0240, "\u{2c7f}"), + (0x0242, "\u{241}"), + (0x0247, "\u{246}"), + (0x0249, "\u{248}"), + (0x024B, "\u{24a}"), + (0x024D, "\u{24c}"), + (0x024F, "\u{24e}"), + (0x0250, "\u{2c6f}"), + (0x0251, "\u{2c6d}"), + (0x0252, "\u{2c70}"), + (0x0253, "\u{181}"), + (0x0254, "\u{186}"), + (0x0256, "\u{189}"), + (0x0257, "\u{18a}"), + (0x0259, "\u{18f}"), + (0x025B, "\u{190}"), + (0x025C, "\u{a7ab}"), + (0x0260, "\u{193}"), + (0x0261, "\u{a7ac}"), + (0x0263, "\u{194}"), + (0x0264, "\u{a7cb}"), + (0x0265, "\u{a78d}"), + (0x0266, "\u{a7aa}"), + (0x0268, "\u{197}"), + (0x0269, "\u{196}"), + (0x026A, "\u{a7ae}"), + (0x026B, "\u{2c62}"), + (0x026C, "\u{a7ad}"), + (0x026F, "\u{19c}"), + (0x0271, "\u{2c6e}"), + (0x0272, "\u{19d}"), + (0x0275, "\u{19f}"), + (0x027D, "\u{2c64}"), + (0x0280, "\u{1a6}"), + (0x0282, "\u{a7c5}"), + (0x0283, "\u{1a9}"), + (0x0287, "\u{a7b1}"), + (0x0288, "\u{1ae}"), + (0x0289, "\u{244}"), + (0x028A, "\u{1b1}"), + (0x028B, "\u{1b2}"), + (0x028C, "\u{245}"), + (0x0292, "\u{1b7}"), + (0x029D, "\u{a7b2}"), + (0x029E, "\u{a7b0}"), + (0x0345, "\u{399}"), + (0x0371, "\u{370}"), + (0x0373, "\u{372}"), + (0x0377, "\u{376}"), + (0x037B, "\u{3fd}"), + (0x037C, "\u{3fe}"), + (0x037D, "\u{3ff}"), + (0x0390, "\u{399}\u{308}\u{301}"), + (0x03AC, "\u{386}"), + (0x03AD, "\u{388}"), + (0x03AE, "\u{389}"), + (0x03AF, "\u{38a}"), + (0x03B0, "\u{3a5}\u{308}\u{301}"), + (0x03B1, "\u{391}"), + (0x03B2, "\u{392}"), + (0x03B3, "\u{393}"), + (0x03B4, "\u{394}"), + (0x03B5, "\u{395}"), + (0x03B6, "\u{396}"), + (0x03B7, "\u{397}"), + (0x03B8, "\u{398}"), + (0x03B9, "\u{399}"), + (0x03BA, "\u{39a}"), + (0x03BB, "\u{39b}"), + (0x03BC, "\u{39c}"), + (0x03BD, "\u{39d}"), + (0x03BE, "\u{39e}"), + (0x03BF, "\u{39f}"), + (0x03C0, "\u{3a0}"), + (0x03C1, "\u{3a1}"), + (0x03C2, "\u{3a3}"), + (0x03C3, "\u{3a3}"), + (0x03C4, "\u{3a4}"), + (0x03C5, "\u{3a5}"), + (0x03C6, "\u{3a6}"), + (0x03C7, "\u{3a7}"), + (0x03C8, "\u{3a8}"), + (0x03C9, "\u{3a9}"), + (0x03CA, "\u{3aa}"), + (0x03CB, "\u{3ab}"), + (0x03CC, "\u{38c}"), + (0x03CD, "\u{38e}"), + (0x03CE, "\u{38f}"), + (0x03D0, "\u{392}"), + (0x03D1, "\u{398}"), + (0x03D5, "\u{3a6}"), + (0x03D6, "\u{3a0}"), + (0x03D7, "\u{3cf}"), + (0x03D9, "\u{3d8}"), + (0x03DB, "\u{3da}"), + (0x03DD, "\u{3dc}"), + (0x03DF, "\u{3de}"), + (0x03E1, "\u{3e0}"), + (0x03E3, "\u{3e2}"), + (0x03E5, "\u{3e4}"), + (0x03E7, "\u{3e6}"), + (0x03E9, "\u{3e8}"), + (0x03EB, "\u{3ea}"), + (0x03ED, "\u{3ec}"), + (0x03EF, "\u{3ee}"), + (0x03F0, "\u{39a}"), + (0x03F1, "\u{3a1}"), + (0x03F2, "\u{3f9}"), + (0x03F3, "\u{37f}"), + (0x03F5, "\u{395}"), + (0x03F8, "\u{3f7}"), + (0x03FB, "\u{3fa}"), + (0x0430, "\u{410}"), + (0x0431, "\u{411}"), + (0x0432, "\u{412}"), + (0x0433, "\u{413}"), + (0x0434, "\u{414}"), + (0x0435, "\u{415}"), + (0x0436, "\u{416}"), + (0x0437, "\u{417}"), + (0x0438, "\u{418}"), + (0x0439, "\u{419}"), + (0x043A, "\u{41a}"), + (0x043B, "\u{41b}"), + (0x043C, "\u{41c}"), + (0x043D, "\u{41d}"), + (0x043E, "\u{41e}"), + (0x043F, "\u{41f}"), + (0x0440, "\u{420}"), + (0x0441, "\u{421}"), + (0x0442, "\u{422}"), + (0x0443, "\u{423}"), + (0x0444, "\u{424}"), + (0x0445, "\u{425}"), + (0x0446, "\u{426}"), + (0x0447, "\u{427}"), + (0x0448, "\u{428}"), + (0x0449, "\u{429}"), + (0x044A, "\u{42a}"), + (0x044B, "\u{42b}"), + (0x044C, "\u{42c}"), + (0x044D, "\u{42d}"), + (0x044E, "\u{42e}"), + (0x044F, "\u{42f}"), + (0x0450, "\u{400}"), + (0x0451, "\u{401}"), + (0x0452, "\u{402}"), + (0x0453, "\u{403}"), + (0x0454, "\u{404}"), + (0x0455, "\u{405}"), + (0x0456, "\u{406}"), + (0x0457, "\u{407}"), + (0x0458, "\u{408}"), + (0x0459, "\u{409}"), + (0x045A, "\u{40a}"), + (0x045B, "\u{40b}"), + (0x045C, "\u{40c}"), + (0x045D, "\u{40d}"), + (0x045E, "\u{40e}"), + (0x045F, "\u{40f}"), + (0x0461, "\u{460}"), + (0x0463, "\u{462}"), + (0x0465, "\u{464}"), + (0x0467, "\u{466}"), + (0x0469, "\u{468}"), + (0x046B, "\u{46a}"), + (0x046D, "\u{46c}"), + (0x046F, "\u{46e}"), + (0x0471, "\u{470}"), + (0x0473, "\u{472}"), + (0x0475, "\u{474}"), + (0x0477, "\u{476}"), + (0x0479, "\u{478}"), + (0x047B, "\u{47a}"), + (0x047D, "\u{47c}"), + (0x047F, "\u{47e}"), + (0x0481, "\u{480}"), + (0x048B, "\u{48a}"), + (0x048D, "\u{48c}"), + (0x048F, "\u{48e}"), + (0x0491, "\u{490}"), + (0x0493, "\u{492}"), + (0x0495, "\u{494}"), + (0x0497, "\u{496}"), + (0x0499, "\u{498}"), + (0x049B, "\u{49a}"), + (0x049D, "\u{49c}"), + (0x049F, "\u{49e}"), + (0x04A1, "\u{4a0}"), + (0x04A3, "\u{4a2}"), + (0x04A5, "\u{4a4}"), + (0x04A7, "\u{4a6}"), + (0x04A9, "\u{4a8}"), + (0x04AB, "\u{4aa}"), + (0x04AD, "\u{4ac}"), + (0x04AF, "\u{4ae}"), + (0x04B1, "\u{4b0}"), + (0x04B3, "\u{4b2}"), + (0x04B5, "\u{4b4}"), + (0x04B7, "\u{4b6}"), + (0x04B9, "\u{4b8}"), + (0x04BB, "\u{4ba}"), + (0x04BD, "\u{4bc}"), + (0x04BF, "\u{4be}"), + (0x04C2, "\u{4c1}"), + (0x04C4, "\u{4c3}"), + (0x04C6, "\u{4c5}"), + (0x04C8, "\u{4c7}"), + (0x04CA, "\u{4c9}"), + (0x04CC, "\u{4cb}"), + (0x04CE, "\u{4cd}"), + (0x04CF, "\u{4c0}"), + (0x04D1, "\u{4d0}"), + (0x04D3, "\u{4d2}"), + (0x04D5, "\u{4d4}"), + (0x04D7, "\u{4d6}"), + (0x04D9, "\u{4d8}"), + (0x04DB, "\u{4da}"), + (0x04DD, "\u{4dc}"), + (0x04DF, "\u{4de}"), + (0x04E1, "\u{4e0}"), + (0x04E3, "\u{4e2}"), + (0x04E5, "\u{4e4}"), + (0x04E7, "\u{4e6}"), + (0x04E9, "\u{4e8}"), + (0x04EB, "\u{4ea}"), + (0x04ED, "\u{4ec}"), + (0x04EF, "\u{4ee}"), + (0x04F1, "\u{4f0}"), + (0x04F3, "\u{4f2}"), + (0x04F5, "\u{4f4}"), + (0x04F7, "\u{4f6}"), + (0x04F9, "\u{4f8}"), + (0x04FB, "\u{4fa}"), + (0x04FD, "\u{4fc}"), + (0x04FF, "\u{4fe}"), + (0x0501, "\u{500}"), + (0x0503, "\u{502}"), + (0x0505, "\u{504}"), + (0x0507, "\u{506}"), + (0x0509, "\u{508}"), + (0x050B, "\u{50a}"), + (0x050D, "\u{50c}"), + (0x050F, "\u{50e}"), + (0x0511, "\u{510}"), + (0x0513, "\u{512}"), + (0x0515, "\u{514}"), + (0x0517, "\u{516}"), + (0x0519, "\u{518}"), + (0x051B, "\u{51a}"), + (0x051D, "\u{51c}"), + (0x051F, "\u{51e}"), + (0x0521, "\u{520}"), + (0x0523, "\u{522}"), + (0x0525, "\u{524}"), + (0x0527, "\u{526}"), + (0x0529, "\u{528}"), + (0x052B, "\u{52a}"), + (0x052D, "\u{52c}"), + (0x052F, "\u{52e}"), + (0x0561, "\u{531}"), + (0x0562, "\u{532}"), + (0x0563, "\u{533}"), + (0x0564, "\u{534}"), + (0x0565, "\u{535}"), + (0x0566, "\u{536}"), + (0x0567, "\u{537}"), + (0x0568, "\u{538}"), + (0x0569, "\u{539}"), + (0x056A, "\u{53a}"), + (0x056B, "\u{53b}"), + (0x056C, "\u{53c}"), + (0x056D, "\u{53d}"), + (0x056E, "\u{53e}"), + (0x056F, "\u{53f}"), + (0x0570, "\u{540}"), + (0x0571, "\u{541}"), + (0x0572, "\u{542}"), + (0x0573, "\u{543}"), + (0x0574, "\u{544}"), + (0x0575, "\u{545}"), + (0x0576, "\u{546}"), + (0x0577, "\u{547}"), + (0x0578, "\u{548}"), + (0x0579, "\u{549}"), + (0x057A, "\u{54a}"), + (0x057B, "\u{54b}"), + (0x057C, "\u{54c}"), + (0x057D, "\u{54d}"), + (0x057E, "\u{54e}"), + (0x057F, "\u{54f}"), + (0x0580, "\u{550}"), + (0x0581, "\u{551}"), + (0x0582, "\u{552}"), + (0x0583, "\u{553}"), + (0x0584, "\u{554}"), + (0x0585, "\u{555}"), + (0x0586, "\u{556}"), + (0x0587, "\u{535}\u{552}"), + (0x10D0, "\u{1c90}"), + (0x10D1, "\u{1c91}"), + (0x10D2, "\u{1c92}"), + (0x10D3, "\u{1c93}"), + (0x10D4, "\u{1c94}"), + (0x10D5, "\u{1c95}"), + (0x10D6, "\u{1c96}"), + (0x10D7, "\u{1c97}"), + (0x10D8, "\u{1c98}"), + (0x10D9, "\u{1c99}"), + (0x10DA, "\u{1c9a}"), + (0x10DB, "\u{1c9b}"), + (0x10DC, "\u{1c9c}"), + (0x10DD, "\u{1c9d}"), + (0x10DE, "\u{1c9e}"), + (0x10DF, "\u{1c9f}"), + (0x10E0, "\u{1ca0}"), + (0x10E1, "\u{1ca1}"), + (0x10E2, "\u{1ca2}"), + (0x10E3, "\u{1ca3}"), + (0x10E4, "\u{1ca4}"), + (0x10E5, "\u{1ca5}"), + (0x10E6, "\u{1ca6}"), + (0x10E7, "\u{1ca7}"), + (0x10E8, "\u{1ca8}"), + (0x10E9, "\u{1ca9}"), + (0x10EA, "\u{1caa}"), + (0x10EB, "\u{1cab}"), + (0x10EC, "\u{1cac}"), + (0x10ED, "\u{1cad}"), + (0x10EE, "\u{1cae}"), + (0x10EF, "\u{1caf}"), + (0x10F0, "\u{1cb0}"), + (0x10F1, "\u{1cb1}"), + (0x10F2, "\u{1cb2}"), + (0x10F3, "\u{1cb3}"), + (0x10F4, "\u{1cb4}"), + (0x10F5, "\u{1cb5}"), + (0x10F6, "\u{1cb6}"), + (0x10F7, "\u{1cb7}"), + (0x10F8, "\u{1cb8}"), + (0x10F9, "\u{1cb9}"), + (0x10FA, "\u{1cba}"), + (0x10FD, "\u{1cbd}"), + (0x10FE, "\u{1cbe}"), + (0x10FF, "\u{1cbf}"), + (0x13F8, "\u{13f0}"), + (0x13F9, "\u{13f1}"), + (0x13FA, "\u{13f2}"), + (0x13FB, "\u{13f3}"), + (0x13FC, "\u{13f4}"), + (0x13FD, "\u{13f5}"), + (0x1C80, "\u{412}"), + (0x1C81, "\u{414}"), + (0x1C82, "\u{41e}"), + (0x1C83, "\u{421}"), + (0x1C84, "\u{422}"), + (0x1C85, "\u{422}"), + (0x1C86, "\u{42a}"), + (0x1C87, "\u{462}"), + (0x1C88, "\u{a64a}"), + (0x1C8A, "\u{1c89}"), + (0x1D79, "\u{a77d}"), + (0x1D7D, "\u{2c63}"), + (0x1D8E, "\u{a7c6}"), + (0x1E01, "\u{1e00}"), + (0x1E03, "\u{1e02}"), + (0x1E05, "\u{1e04}"), + (0x1E07, "\u{1e06}"), + (0x1E09, "\u{1e08}"), + (0x1E0B, "\u{1e0a}"), + (0x1E0D, "\u{1e0c}"), + (0x1E0F, "\u{1e0e}"), + (0x1E11, "\u{1e10}"), + (0x1E13, "\u{1e12}"), + (0x1E15, "\u{1e14}"), + (0x1E17, "\u{1e16}"), + (0x1E19, "\u{1e18}"), + (0x1E1B, "\u{1e1a}"), + (0x1E1D, "\u{1e1c}"), + (0x1E1F, "\u{1e1e}"), + (0x1E21, "\u{1e20}"), + (0x1E23, "\u{1e22}"), + (0x1E25, "\u{1e24}"), + (0x1E27, "\u{1e26}"), + (0x1E29, "\u{1e28}"), + (0x1E2B, "\u{1e2a}"), + (0x1E2D, "\u{1e2c}"), + (0x1E2F, "\u{1e2e}"), + (0x1E31, "\u{1e30}"), + (0x1E33, "\u{1e32}"), + (0x1E35, "\u{1e34}"), + (0x1E37, "\u{1e36}"), + (0x1E39, "\u{1e38}"), + (0x1E3B, "\u{1e3a}"), + (0x1E3D, "\u{1e3c}"), + (0x1E3F, "\u{1e3e}"), + (0x1E41, "\u{1e40}"), + (0x1E43, "\u{1e42}"), + (0x1E45, "\u{1e44}"), + (0x1E47, "\u{1e46}"), + (0x1E49, "\u{1e48}"), + (0x1E4B, "\u{1e4a}"), + (0x1E4D, "\u{1e4c}"), + (0x1E4F, "\u{1e4e}"), + (0x1E51, "\u{1e50}"), + (0x1E53, "\u{1e52}"), + (0x1E55, "\u{1e54}"), + (0x1E57, "\u{1e56}"), + (0x1E59, "\u{1e58}"), + (0x1E5B, "\u{1e5a}"), + (0x1E5D, "\u{1e5c}"), + (0x1E5F, "\u{1e5e}"), + (0x1E61, "\u{1e60}"), + (0x1E63, "\u{1e62}"), + (0x1E65, "\u{1e64}"), + (0x1E67, "\u{1e66}"), + (0x1E69, "\u{1e68}"), + (0x1E6B, "\u{1e6a}"), + (0x1E6D, "\u{1e6c}"), + (0x1E6F, "\u{1e6e}"), + (0x1E71, "\u{1e70}"), + (0x1E73, "\u{1e72}"), + (0x1E75, "\u{1e74}"), + (0x1E77, "\u{1e76}"), + (0x1E79, "\u{1e78}"), + (0x1E7B, "\u{1e7a}"), + (0x1E7D, "\u{1e7c}"), + (0x1E7F, "\u{1e7e}"), + (0x1E81, "\u{1e80}"), + (0x1E83, "\u{1e82}"), + (0x1E85, "\u{1e84}"), + (0x1E87, "\u{1e86}"), + (0x1E89, "\u{1e88}"), + (0x1E8B, "\u{1e8a}"), + (0x1E8D, "\u{1e8c}"), + (0x1E8F, "\u{1e8e}"), + (0x1E91, "\u{1e90}"), + (0x1E93, "\u{1e92}"), + (0x1E95, "\u{1e94}"), + (0x1E96, "\u{48}\u{331}"), + (0x1E97, "\u{54}\u{308}"), + (0x1E98, "\u{57}\u{30a}"), + (0x1E99, "\u{59}\u{30a}"), + (0x1E9A, "\u{41}\u{2be}"), + (0x1E9B, "\u{1e60}"), + (0x1EA1, "\u{1ea0}"), + (0x1EA3, "\u{1ea2}"), + (0x1EA5, "\u{1ea4}"), + (0x1EA7, "\u{1ea6}"), + (0x1EA9, "\u{1ea8}"), + (0x1EAB, "\u{1eaa}"), + (0x1EAD, "\u{1eac}"), + (0x1EAF, "\u{1eae}"), + (0x1EB1, "\u{1eb0}"), + (0x1EB3, "\u{1eb2}"), + (0x1EB5, "\u{1eb4}"), + (0x1EB7, "\u{1eb6}"), + (0x1EB9, "\u{1eb8}"), + (0x1EBB, "\u{1eba}"), + (0x1EBD, "\u{1ebc}"), + (0x1EBF, "\u{1ebe}"), + (0x1EC1, "\u{1ec0}"), + (0x1EC3, "\u{1ec2}"), + (0x1EC5, "\u{1ec4}"), + (0x1EC7, "\u{1ec6}"), + (0x1EC9, "\u{1ec8}"), + (0x1ECB, "\u{1eca}"), + (0x1ECD, "\u{1ecc}"), + (0x1ECF, "\u{1ece}"), + (0x1ED1, "\u{1ed0}"), + (0x1ED3, "\u{1ed2}"), + (0x1ED5, "\u{1ed4}"), + (0x1ED7, "\u{1ed6}"), + (0x1ED9, "\u{1ed8}"), + (0x1EDB, "\u{1eda}"), + (0x1EDD, "\u{1edc}"), + (0x1EDF, "\u{1ede}"), + (0x1EE1, "\u{1ee0}"), + (0x1EE3, "\u{1ee2}"), + (0x1EE5, "\u{1ee4}"), + (0x1EE7, "\u{1ee6}"), + (0x1EE9, "\u{1ee8}"), + (0x1EEB, "\u{1eea}"), + (0x1EED, "\u{1eec}"), + (0x1EEF, "\u{1eee}"), + (0x1EF1, "\u{1ef0}"), + (0x1EF3, "\u{1ef2}"), + (0x1EF5, "\u{1ef4}"), + (0x1EF7, "\u{1ef6}"), + (0x1EF9, "\u{1ef8}"), + (0x1EFB, "\u{1efa}"), + (0x1EFD, "\u{1efc}"), + (0x1EFF, "\u{1efe}"), + (0x1F00, "\u{1f08}"), + (0x1F01, "\u{1f09}"), + (0x1F02, "\u{1f0a}"), + (0x1F03, "\u{1f0b}"), + (0x1F04, "\u{1f0c}"), + (0x1F05, "\u{1f0d}"), + (0x1F06, "\u{1f0e}"), + (0x1F07, "\u{1f0f}"), + (0x1F10, "\u{1f18}"), + (0x1F11, "\u{1f19}"), + (0x1F12, "\u{1f1a}"), + (0x1F13, "\u{1f1b}"), + (0x1F14, "\u{1f1c}"), + (0x1F15, "\u{1f1d}"), + (0x1F20, "\u{1f28}"), + (0x1F21, "\u{1f29}"), + (0x1F22, "\u{1f2a}"), + (0x1F23, "\u{1f2b}"), + (0x1F24, "\u{1f2c}"), + (0x1F25, "\u{1f2d}"), + (0x1F26, "\u{1f2e}"), + (0x1F27, "\u{1f2f}"), + (0x1F30, "\u{1f38}"), + (0x1F31, "\u{1f39}"), + (0x1F32, "\u{1f3a}"), + (0x1F33, "\u{1f3b}"), + (0x1F34, "\u{1f3c}"), + (0x1F35, "\u{1f3d}"), + (0x1F36, "\u{1f3e}"), + (0x1F37, "\u{1f3f}"), + (0x1F40, "\u{1f48}"), + (0x1F41, "\u{1f49}"), + (0x1F42, "\u{1f4a}"), + (0x1F43, "\u{1f4b}"), + (0x1F44, "\u{1f4c}"), + (0x1F45, "\u{1f4d}"), + (0x1F50, "\u{3a5}\u{313}"), + (0x1F51, "\u{1f59}"), + (0x1F52, "\u{3a5}\u{313}\u{300}"), + (0x1F53, "\u{1f5b}"), + (0x1F54, "\u{3a5}\u{313}\u{301}"), + (0x1F55, "\u{1f5d}"), + (0x1F56, "\u{3a5}\u{313}\u{342}"), + (0x1F57, "\u{1f5f}"), + (0x1F60, "\u{1f68}"), + (0x1F61, "\u{1f69}"), + (0x1F62, "\u{1f6a}"), + (0x1F63, "\u{1f6b}"), + (0x1F64, "\u{1f6c}"), + (0x1F65, "\u{1f6d}"), + (0x1F66, "\u{1f6e}"), + (0x1F67, "\u{1f6f}"), + (0x1F70, "\u{1fba}"), + (0x1F71, "\u{1fbb}"), + (0x1F72, "\u{1fc8}"), + (0x1F73, "\u{1fc9}"), + (0x1F74, "\u{1fca}"), + (0x1F75, "\u{1fcb}"), + (0x1F76, "\u{1fda}"), + (0x1F77, "\u{1fdb}"), + (0x1F78, "\u{1ff8}"), + (0x1F79, "\u{1ff9}"), + (0x1F7A, "\u{1fea}"), + (0x1F7B, "\u{1feb}"), + (0x1F7C, "\u{1ffa}"), + (0x1F7D, "\u{1ffb}"), + (0x1F80, "\u{1f08}\u{399}"), + (0x1F81, "\u{1f09}\u{399}"), + (0x1F82, "\u{1f0a}\u{399}"), + (0x1F83, "\u{1f0b}\u{399}"), + (0x1F84, "\u{1f0c}\u{399}"), + (0x1F85, "\u{1f0d}\u{399}"), + (0x1F86, "\u{1f0e}\u{399}"), + (0x1F87, "\u{1f0f}\u{399}"), + (0x1F88, "\u{1f08}\u{399}"), + (0x1F89, "\u{1f09}\u{399}"), + (0x1F8A, "\u{1f0a}\u{399}"), + (0x1F8B, "\u{1f0b}\u{399}"), + (0x1F8C, "\u{1f0c}\u{399}"), + (0x1F8D, "\u{1f0d}\u{399}"), + (0x1F8E, "\u{1f0e}\u{399}"), + (0x1F8F, "\u{1f0f}\u{399}"), + (0x1F90, "\u{1f28}\u{399}"), + (0x1F91, "\u{1f29}\u{399}"), + (0x1F92, "\u{1f2a}\u{399}"), + (0x1F93, "\u{1f2b}\u{399}"), + (0x1F94, "\u{1f2c}\u{399}"), + (0x1F95, "\u{1f2d}\u{399}"), + (0x1F96, "\u{1f2e}\u{399}"), + (0x1F97, "\u{1f2f}\u{399}"), + (0x1F98, "\u{1f28}\u{399}"), + (0x1F99, "\u{1f29}\u{399}"), + (0x1F9A, "\u{1f2a}\u{399}"), + (0x1F9B, "\u{1f2b}\u{399}"), + (0x1F9C, "\u{1f2c}\u{399}"), + (0x1F9D, "\u{1f2d}\u{399}"), + (0x1F9E, "\u{1f2e}\u{399}"), + (0x1F9F, "\u{1f2f}\u{399}"), + (0x1FA0, "\u{1f68}\u{399}"), + (0x1FA1, "\u{1f69}\u{399}"), + (0x1FA2, "\u{1f6a}\u{399}"), + (0x1FA3, "\u{1f6b}\u{399}"), + (0x1FA4, "\u{1f6c}\u{399}"), + (0x1FA5, "\u{1f6d}\u{399}"), + (0x1FA6, "\u{1f6e}\u{399}"), + (0x1FA7, "\u{1f6f}\u{399}"), + (0x1FA8, "\u{1f68}\u{399}"), + (0x1FA9, "\u{1f69}\u{399}"), + (0x1FAA, "\u{1f6a}\u{399}"), + (0x1FAB, "\u{1f6b}\u{399}"), + (0x1FAC, "\u{1f6c}\u{399}"), + (0x1FAD, "\u{1f6d}\u{399}"), + (0x1FAE, "\u{1f6e}\u{399}"), + (0x1FAF, "\u{1f6f}\u{399}"), + (0x1FB0, "\u{1fb8}"), + (0x1FB1, "\u{1fb9}"), + (0x1FB2, "\u{1fba}\u{399}"), + (0x1FB3, "\u{391}\u{399}"), + (0x1FB4, "\u{386}\u{399}"), + (0x1FB6, "\u{391}\u{342}"), + (0x1FB7, "\u{391}\u{342}\u{399}"), + (0x1FBC, "\u{391}\u{399}"), + (0x1FBE, "\u{399}"), + (0x1FC2, "\u{1fca}\u{399}"), + (0x1FC3, "\u{397}\u{399}"), + (0x1FC4, "\u{389}\u{399}"), + (0x1FC6, "\u{397}\u{342}"), + (0x1FC7, "\u{397}\u{342}\u{399}"), + (0x1FCC, "\u{397}\u{399}"), + (0x1FD0, "\u{1fd8}"), + (0x1FD1, "\u{1fd9}"), + (0x1FD2, "\u{399}\u{308}\u{300}"), + (0x1FD3, "\u{399}\u{308}\u{301}"), + (0x1FD6, "\u{399}\u{342}"), + (0x1FD7, "\u{399}\u{308}\u{342}"), + (0x1FE0, "\u{1fe8}"), + (0x1FE1, "\u{1fe9}"), + (0x1FE2, "\u{3a5}\u{308}\u{300}"), + (0x1FE3, "\u{3a5}\u{308}\u{301}"), + (0x1FE4, "\u{3a1}\u{313}"), + (0x1FE5, "\u{1fec}"), + (0x1FE6, "\u{3a5}\u{342}"), + (0x1FE7, "\u{3a5}\u{308}\u{342}"), + (0x1FF2, "\u{1ffa}\u{399}"), + (0x1FF3, "\u{3a9}\u{399}"), + (0x1FF4, "\u{38f}\u{399}"), + (0x1FF6, "\u{3a9}\u{342}"), + (0x1FF7, "\u{3a9}\u{342}\u{399}"), + (0x1FFC, "\u{3a9}\u{399}"), + (0x214E, "\u{2132}"), + (0x2170, "\u{2160}"), + (0x2171, "\u{2161}"), + (0x2172, "\u{2162}"), + (0x2173, "\u{2163}"), + (0x2174, "\u{2164}"), + (0x2175, "\u{2165}"), + (0x2176, "\u{2166}"), + (0x2177, "\u{2167}"), + (0x2178, "\u{2168}"), + (0x2179, "\u{2169}"), + (0x217A, "\u{216a}"), + (0x217B, "\u{216b}"), + (0x217C, "\u{216c}"), + (0x217D, "\u{216d}"), + (0x217E, "\u{216e}"), + (0x217F, "\u{216f}"), + (0x2184, "\u{2183}"), + (0x24D0, "\u{24b6}"), + (0x24D1, "\u{24b7}"), + (0x24D2, "\u{24b8}"), + (0x24D3, "\u{24b9}"), + (0x24D4, "\u{24ba}"), + (0x24D5, "\u{24bb}"), + (0x24D6, "\u{24bc}"), + (0x24D7, "\u{24bd}"), + (0x24D8, "\u{24be}"), + (0x24D9, "\u{24bf}"), + (0x24DA, "\u{24c0}"), + (0x24DB, "\u{24c1}"), + (0x24DC, "\u{24c2}"), + (0x24DD, "\u{24c3}"), + (0x24DE, "\u{24c4}"), + (0x24DF, "\u{24c5}"), + (0x24E0, "\u{24c6}"), + (0x24E1, "\u{24c7}"), + (0x24E2, "\u{24c8}"), + (0x24E3, "\u{24c9}"), + (0x24E4, "\u{24ca}"), + (0x24E5, "\u{24cb}"), + (0x24E6, "\u{24cc}"), + (0x24E7, "\u{24cd}"), + (0x24E8, "\u{24ce}"), + (0x24E9, "\u{24cf}"), + (0x2C30, "\u{2c00}"), + (0x2C31, "\u{2c01}"), + (0x2C32, "\u{2c02}"), + (0x2C33, "\u{2c03}"), + (0x2C34, "\u{2c04}"), + (0x2C35, "\u{2c05}"), + (0x2C36, "\u{2c06}"), + (0x2C37, "\u{2c07}"), + (0x2C38, "\u{2c08}"), + (0x2C39, "\u{2c09}"), + (0x2C3A, "\u{2c0a}"), + (0x2C3B, "\u{2c0b}"), + (0x2C3C, "\u{2c0c}"), + (0x2C3D, "\u{2c0d}"), + (0x2C3E, "\u{2c0e}"), + (0x2C3F, "\u{2c0f}"), + (0x2C40, "\u{2c10}"), + (0x2C41, "\u{2c11}"), + (0x2C42, "\u{2c12}"), + (0x2C43, "\u{2c13}"), + (0x2C44, "\u{2c14}"), + (0x2C45, "\u{2c15}"), + (0x2C46, "\u{2c16}"), + (0x2C47, "\u{2c17}"), + (0x2C48, "\u{2c18}"), + (0x2C49, "\u{2c19}"), + (0x2C4A, "\u{2c1a}"), + (0x2C4B, "\u{2c1b}"), + (0x2C4C, "\u{2c1c}"), + (0x2C4D, "\u{2c1d}"), + (0x2C4E, "\u{2c1e}"), + (0x2C4F, "\u{2c1f}"), + (0x2C50, "\u{2c20}"), + (0x2C51, "\u{2c21}"), + (0x2C52, "\u{2c22}"), + (0x2C53, "\u{2c23}"), + (0x2C54, "\u{2c24}"), + (0x2C55, "\u{2c25}"), + (0x2C56, "\u{2c26}"), + (0x2C57, "\u{2c27}"), + (0x2C58, "\u{2c28}"), + (0x2C59, "\u{2c29}"), + (0x2C5A, "\u{2c2a}"), + (0x2C5B, "\u{2c2b}"), + (0x2C5C, "\u{2c2c}"), + (0x2C5D, "\u{2c2d}"), + (0x2C5E, "\u{2c2e}"), + (0x2C5F, "\u{2c2f}"), + (0x2C61, "\u{2c60}"), + (0x2C65, "\u{23a}"), + (0x2C66, "\u{23e}"), + (0x2C68, "\u{2c67}"), + (0x2C6A, "\u{2c69}"), + (0x2C6C, "\u{2c6b}"), + (0x2C73, "\u{2c72}"), + (0x2C76, "\u{2c75}"), + (0x2C81, "\u{2c80}"), + (0x2C83, "\u{2c82}"), + (0x2C85, "\u{2c84}"), + (0x2C87, "\u{2c86}"), + (0x2C89, "\u{2c88}"), + (0x2C8B, "\u{2c8a}"), + (0x2C8D, "\u{2c8c}"), + (0x2C8F, "\u{2c8e}"), + (0x2C91, "\u{2c90}"), + (0x2C93, "\u{2c92}"), + (0x2C95, "\u{2c94}"), + (0x2C97, "\u{2c96}"), + (0x2C99, "\u{2c98}"), + (0x2C9B, "\u{2c9a}"), + (0x2C9D, "\u{2c9c}"), + (0x2C9F, "\u{2c9e}"), + (0x2CA1, "\u{2ca0}"), + (0x2CA3, "\u{2ca2}"), + (0x2CA5, "\u{2ca4}"), + (0x2CA7, "\u{2ca6}"), + (0x2CA9, "\u{2ca8}"), + (0x2CAB, "\u{2caa}"), + (0x2CAD, "\u{2cac}"), + (0x2CAF, "\u{2cae}"), + (0x2CB1, "\u{2cb0}"), + (0x2CB3, "\u{2cb2}"), + (0x2CB5, "\u{2cb4}"), + (0x2CB7, "\u{2cb6}"), + (0x2CB9, "\u{2cb8}"), + (0x2CBB, "\u{2cba}"), + (0x2CBD, "\u{2cbc}"), + (0x2CBF, "\u{2cbe}"), + (0x2CC1, "\u{2cc0}"), + (0x2CC3, "\u{2cc2}"), + (0x2CC5, "\u{2cc4}"), + (0x2CC7, "\u{2cc6}"), + (0x2CC9, "\u{2cc8}"), + (0x2CCB, "\u{2cca}"), + (0x2CCD, "\u{2ccc}"), + (0x2CCF, "\u{2cce}"), + (0x2CD1, "\u{2cd0}"), + (0x2CD3, "\u{2cd2}"), + (0x2CD5, "\u{2cd4}"), + (0x2CD7, "\u{2cd6}"), + (0x2CD9, "\u{2cd8}"), + (0x2CDB, "\u{2cda}"), + (0x2CDD, "\u{2cdc}"), + (0x2CDF, "\u{2cde}"), + (0x2CE1, "\u{2ce0}"), + (0x2CE3, "\u{2ce2}"), + (0x2CEC, "\u{2ceb}"), + (0x2CEE, "\u{2ced}"), + (0x2CF3, "\u{2cf2}"), + (0x2D00, "\u{10a0}"), + (0x2D01, "\u{10a1}"), + (0x2D02, "\u{10a2}"), + (0x2D03, "\u{10a3}"), + (0x2D04, "\u{10a4}"), + (0x2D05, "\u{10a5}"), + (0x2D06, "\u{10a6}"), + (0x2D07, "\u{10a7}"), + (0x2D08, "\u{10a8}"), + (0x2D09, "\u{10a9}"), + (0x2D0A, "\u{10aa}"), + (0x2D0B, "\u{10ab}"), + (0x2D0C, "\u{10ac}"), + (0x2D0D, "\u{10ad}"), + (0x2D0E, "\u{10ae}"), + (0x2D0F, "\u{10af}"), + (0x2D10, "\u{10b0}"), + (0x2D11, "\u{10b1}"), + (0x2D12, "\u{10b2}"), + (0x2D13, "\u{10b3}"), + (0x2D14, "\u{10b4}"), + (0x2D15, "\u{10b5}"), + (0x2D16, "\u{10b6}"), + (0x2D17, "\u{10b7}"), + (0x2D18, "\u{10b8}"), + (0x2D19, "\u{10b9}"), + (0x2D1A, "\u{10ba}"), + (0x2D1B, "\u{10bb}"), + (0x2D1C, "\u{10bc}"), + (0x2D1D, "\u{10bd}"), + (0x2D1E, "\u{10be}"), + (0x2D1F, "\u{10bf}"), + (0x2D20, "\u{10c0}"), + (0x2D21, "\u{10c1}"), + (0x2D22, "\u{10c2}"), + (0x2D23, "\u{10c3}"), + (0x2D24, "\u{10c4}"), + (0x2D25, "\u{10c5}"), + (0x2D27, "\u{10c7}"), + (0x2D2D, "\u{10cd}"), + (0xA641, "\u{a640}"), + (0xA643, "\u{a642}"), + (0xA645, "\u{a644}"), + (0xA647, "\u{a646}"), + (0xA649, "\u{a648}"), + (0xA64B, "\u{a64a}"), + (0xA64D, "\u{a64c}"), + (0xA64F, "\u{a64e}"), + (0xA651, "\u{a650}"), + (0xA653, "\u{a652}"), + (0xA655, "\u{a654}"), + (0xA657, "\u{a656}"), + (0xA659, "\u{a658}"), + (0xA65B, "\u{a65a}"), + (0xA65D, "\u{a65c}"), + (0xA65F, "\u{a65e}"), + (0xA661, "\u{a660}"), + (0xA663, "\u{a662}"), + (0xA665, "\u{a664}"), + (0xA667, "\u{a666}"), + (0xA669, "\u{a668}"), + (0xA66B, "\u{a66a}"), + (0xA66D, "\u{a66c}"), + (0xA681, "\u{a680}"), + (0xA683, "\u{a682}"), + (0xA685, "\u{a684}"), + (0xA687, "\u{a686}"), + (0xA689, "\u{a688}"), + (0xA68B, "\u{a68a}"), + (0xA68D, "\u{a68c}"), + (0xA68F, "\u{a68e}"), + (0xA691, "\u{a690}"), + (0xA693, "\u{a692}"), + (0xA695, "\u{a694}"), + (0xA697, "\u{a696}"), + (0xA699, "\u{a698}"), + (0xA69B, "\u{a69a}"), + (0xA723, "\u{a722}"), + (0xA725, "\u{a724}"), + (0xA727, "\u{a726}"), + (0xA729, "\u{a728}"), + (0xA72B, "\u{a72a}"), + (0xA72D, "\u{a72c}"), + (0xA72F, "\u{a72e}"), + (0xA733, "\u{a732}"), + (0xA735, "\u{a734}"), + (0xA737, "\u{a736}"), + (0xA739, "\u{a738}"), + (0xA73B, "\u{a73a}"), + (0xA73D, "\u{a73c}"), + (0xA73F, "\u{a73e}"), + (0xA741, "\u{a740}"), + (0xA743, "\u{a742}"), + (0xA745, "\u{a744}"), + (0xA747, "\u{a746}"), + (0xA749, "\u{a748}"), + (0xA74B, "\u{a74a}"), + (0xA74D, "\u{a74c}"), + (0xA74F, "\u{a74e}"), + (0xA751, "\u{a750}"), + (0xA753, "\u{a752}"), + (0xA755, "\u{a754}"), + (0xA757, "\u{a756}"), + (0xA759, "\u{a758}"), + (0xA75B, "\u{a75a}"), + (0xA75D, "\u{a75c}"), + (0xA75F, "\u{a75e}"), + (0xA761, "\u{a760}"), + (0xA763, "\u{a762}"), + (0xA765, "\u{a764}"), + (0xA767, "\u{a766}"), + (0xA769, "\u{a768}"), + (0xA76B, "\u{a76a}"), + (0xA76D, "\u{a76c}"), + (0xA76F, "\u{a76e}"), + (0xA77A, "\u{a779}"), + (0xA77C, "\u{a77b}"), + (0xA77F, "\u{a77e}"), + (0xA781, "\u{a780}"), + (0xA783, "\u{a782}"), + (0xA785, "\u{a784}"), + (0xA787, "\u{a786}"), + (0xA78C, "\u{a78b}"), + (0xA791, "\u{a790}"), + (0xA793, "\u{a792}"), + (0xA794, "\u{a7c4}"), + (0xA797, "\u{a796}"), + (0xA799, "\u{a798}"), + (0xA79B, "\u{a79a}"), + (0xA79D, "\u{a79c}"), + (0xA79F, "\u{a79e}"), + (0xA7A1, "\u{a7a0}"), + (0xA7A3, "\u{a7a2}"), + (0xA7A5, "\u{a7a4}"), + (0xA7A7, "\u{a7a6}"), + (0xA7A9, "\u{a7a8}"), + (0xA7B5, "\u{a7b4}"), + (0xA7B7, "\u{a7b6}"), + (0xA7B9, "\u{a7b8}"), + (0xA7BB, "\u{a7ba}"), + (0xA7BD, "\u{a7bc}"), + (0xA7BF, "\u{a7be}"), + (0xA7C1, "\u{a7c0}"), + (0xA7C3, "\u{a7c2}"), + (0xA7C8, "\u{a7c7}"), + (0xA7CA, "\u{a7c9}"), + (0xA7CD, "\u{a7cc}"), + (0xA7D1, "\u{a7d0}"), + (0xA7D7, "\u{a7d6}"), + (0xA7D9, "\u{a7d8}"), + (0xA7DB, "\u{a7da}"), + (0xA7F6, "\u{a7f5}"), + (0xAB53, "\u{a7b3}"), + (0xAB70, "\u{13a0}"), + (0xAB71, "\u{13a1}"), + (0xAB72, "\u{13a2}"), + (0xAB73, "\u{13a3}"), + (0xAB74, "\u{13a4}"), + (0xAB75, "\u{13a5}"), + (0xAB76, "\u{13a6}"), + (0xAB77, "\u{13a7}"), + (0xAB78, "\u{13a8}"), + (0xAB79, "\u{13a9}"), + (0xAB7A, "\u{13aa}"), + (0xAB7B, "\u{13ab}"), + (0xAB7C, "\u{13ac}"), + (0xAB7D, "\u{13ad}"), + (0xAB7E, "\u{13ae}"), + (0xAB7F, "\u{13af}"), + (0xAB80, "\u{13b0}"), + (0xAB81, "\u{13b1}"), + (0xAB82, "\u{13b2}"), + (0xAB83, "\u{13b3}"), + (0xAB84, "\u{13b4}"), + (0xAB85, "\u{13b5}"), + (0xAB86, "\u{13b6}"), + (0xAB87, "\u{13b7}"), + (0xAB88, "\u{13b8}"), + (0xAB89, "\u{13b9}"), + (0xAB8A, "\u{13ba}"), + (0xAB8B, "\u{13bb}"), + (0xAB8C, "\u{13bc}"), + (0xAB8D, "\u{13bd}"), + (0xAB8E, "\u{13be}"), + (0xAB8F, "\u{13bf}"), + (0xAB90, "\u{13c0}"), + (0xAB91, "\u{13c1}"), + (0xAB92, "\u{13c2}"), + (0xAB93, "\u{13c3}"), + (0xAB94, "\u{13c4}"), + (0xAB95, "\u{13c5}"), + (0xAB96, "\u{13c6}"), + (0xAB97, "\u{13c7}"), + (0xAB98, "\u{13c8}"), + (0xAB99, "\u{13c9}"), + (0xAB9A, "\u{13ca}"), + (0xAB9B, "\u{13cb}"), + (0xAB9C, "\u{13cc}"), + (0xAB9D, "\u{13cd}"), + (0xAB9E, "\u{13ce}"), + (0xAB9F, "\u{13cf}"), + (0xABA0, "\u{13d0}"), + (0xABA1, "\u{13d1}"), + (0xABA2, "\u{13d2}"), + (0xABA3, "\u{13d3}"), + (0xABA4, "\u{13d4}"), + (0xABA5, "\u{13d5}"), + (0xABA6, "\u{13d6}"), + (0xABA7, "\u{13d7}"), + (0xABA8, "\u{13d8}"), + (0xABA9, "\u{13d9}"), + (0xABAA, "\u{13da}"), + (0xABAB, "\u{13db}"), + (0xABAC, "\u{13dc}"), + (0xABAD, "\u{13dd}"), + (0xABAE, "\u{13de}"), + (0xABAF, "\u{13df}"), + (0xABB0, "\u{13e0}"), + (0xABB1, "\u{13e1}"), + (0xABB2, "\u{13e2}"), + (0xABB3, "\u{13e3}"), + (0xABB4, "\u{13e4}"), + (0xABB5, "\u{13e5}"), + (0xABB6, "\u{13e6}"), + (0xABB7, "\u{13e7}"), + (0xABB8, "\u{13e8}"), + (0xABB9, "\u{13e9}"), + (0xABBA, "\u{13ea}"), + (0xABBB, "\u{13eb}"), + (0xABBC, "\u{13ec}"), + (0xABBD, "\u{13ed}"), + (0xABBE, "\u{13ee}"), + (0xABBF, "\u{13ef}"), + (0xFB00, "\u{46}\u{46}"), + (0xFB01, "\u{46}\u{49}"), + (0xFB02, "\u{46}\u{4c}"), + (0xFB03, "\u{46}\u{46}\u{49}"), + (0xFB04, "\u{46}\u{46}\u{4c}"), + (0xFB05, "\u{53}\u{54}"), + (0xFB06, "\u{53}\u{54}"), + (0xFB13, "\u{544}\u{546}"), + (0xFB14, "\u{544}\u{535}"), + (0xFB15, "\u{544}\u{53b}"), + (0xFB16, "\u{54e}\u{546}"), + (0xFB17, "\u{544}\u{53d}"), + (0xFF41, "\u{ff21}"), + (0xFF42, "\u{ff22}"), + (0xFF43, "\u{ff23}"), + (0xFF44, "\u{ff24}"), + (0xFF45, "\u{ff25}"), + (0xFF46, "\u{ff26}"), + (0xFF47, "\u{ff27}"), + (0xFF48, "\u{ff28}"), + (0xFF49, "\u{ff29}"), + (0xFF4A, "\u{ff2a}"), + (0xFF4B, "\u{ff2b}"), + (0xFF4C, "\u{ff2c}"), + (0xFF4D, "\u{ff2d}"), + (0xFF4E, "\u{ff2e}"), + (0xFF4F, "\u{ff2f}"), + (0xFF50, "\u{ff30}"), + (0xFF51, "\u{ff31}"), + (0xFF52, "\u{ff32}"), + (0xFF53, "\u{ff33}"), + (0xFF54, "\u{ff34}"), + (0xFF55, "\u{ff35}"), + (0xFF56, "\u{ff36}"), + (0xFF57, "\u{ff37}"), + (0xFF58, "\u{ff38}"), + (0xFF59, "\u{ff39}"), + (0xFF5A, "\u{ff3a}"), + (0x10428, "\u{10400}"), + (0x10429, "\u{10401}"), + (0x1042A, "\u{10402}"), + (0x1042B, "\u{10403}"), + (0x1042C, "\u{10404}"), + (0x1042D, "\u{10405}"), + (0x1042E, "\u{10406}"), + (0x1042F, "\u{10407}"), + (0x10430, "\u{10408}"), + (0x10431, "\u{10409}"), + (0x10432, "\u{1040a}"), + (0x10433, "\u{1040b}"), + (0x10434, "\u{1040c}"), + (0x10435, "\u{1040d}"), + (0x10436, "\u{1040e}"), + (0x10437, "\u{1040f}"), + (0x10438, "\u{10410}"), + (0x10439, "\u{10411}"), + (0x1043A, "\u{10412}"), + (0x1043B, "\u{10413}"), + (0x1043C, "\u{10414}"), + (0x1043D, "\u{10415}"), + (0x1043E, "\u{10416}"), + (0x1043F, "\u{10417}"), + (0x10440, "\u{10418}"), + (0x10441, "\u{10419}"), + (0x10442, "\u{1041a}"), + (0x10443, "\u{1041b}"), + (0x10444, "\u{1041c}"), + (0x10445, "\u{1041d}"), + (0x10446, "\u{1041e}"), + (0x10447, "\u{1041f}"), + (0x10448, "\u{10420}"), + (0x10449, "\u{10421}"), + (0x1044A, "\u{10422}"), + (0x1044B, "\u{10423}"), + (0x1044C, "\u{10424}"), + (0x1044D, "\u{10425}"), + (0x1044E, "\u{10426}"), + (0x1044F, "\u{10427}"), + (0x104D8, "\u{104b0}"), + (0x104D9, "\u{104b1}"), + (0x104DA, "\u{104b2}"), + (0x104DB, "\u{104b3}"), + (0x104DC, "\u{104b4}"), + (0x104DD, "\u{104b5}"), + (0x104DE, "\u{104b6}"), + (0x104DF, "\u{104b7}"), + (0x104E0, "\u{104b8}"), + (0x104E1, "\u{104b9}"), + (0x104E2, "\u{104ba}"), + (0x104E3, "\u{104bb}"), + (0x104E4, "\u{104bc}"), + (0x104E5, "\u{104bd}"), + (0x104E6, "\u{104be}"), + (0x104E7, "\u{104bf}"), + (0x104E8, "\u{104c0}"), + (0x104E9, "\u{104c1}"), + (0x104EA, "\u{104c2}"), + (0x104EB, "\u{104c3}"), + (0x104EC, "\u{104c4}"), + (0x104ED, "\u{104c5}"), + (0x104EE, "\u{104c6}"), + (0x104EF, "\u{104c7}"), + (0x104F0, "\u{104c8}"), + (0x104F1, "\u{104c9}"), + (0x104F2, "\u{104ca}"), + (0x104F3, "\u{104cb}"), + (0x104F4, "\u{104cc}"), + (0x104F5, "\u{104cd}"), + (0x104F6, "\u{104ce}"), + (0x104F7, "\u{104cf}"), + (0x104F8, "\u{104d0}"), + (0x104F9, "\u{104d1}"), + (0x104FA, "\u{104d2}"), + (0x104FB, "\u{104d3}"), + (0x10597, "\u{10570}"), + (0x10598, "\u{10571}"), + (0x10599, "\u{10572}"), + (0x1059A, "\u{10573}"), + (0x1059B, "\u{10574}"), + (0x1059C, "\u{10575}"), + (0x1059D, "\u{10576}"), + (0x1059E, "\u{10577}"), + (0x1059F, "\u{10578}"), + (0x105A0, "\u{10579}"), + (0x105A1, "\u{1057a}"), + (0x105A3, "\u{1057c}"), + (0x105A4, "\u{1057d}"), + (0x105A5, "\u{1057e}"), + (0x105A6, "\u{1057f}"), + (0x105A7, "\u{10580}"), + (0x105A8, "\u{10581}"), + (0x105A9, "\u{10582}"), + (0x105AA, "\u{10583}"), + (0x105AB, "\u{10584}"), + (0x105AC, "\u{10585}"), + (0x105AD, "\u{10586}"), + (0x105AE, "\u{10587}"), + (0x105AF, "\u{10588}"), + (0x105B0, "\u{10589}"), + (0x105B1, "\u{1058a}"), + (0x105B3, "\u{1058c}"), + (0x105B4, "\u{1058d}"), + (0x105B5, "\u{1058e}"), + (0x105B6, "\u{1058f}"), + (0x105B7, "\u{10590}"), + (0x105B8, "\u{10591}"), + (0x105B9, "\u{10592}"), + (0x105BB, "\u{10594}"), + (0x105BC, "\u{10595}"), + (0x10CC0, "\u{10c80}"), + (0x10CC1, "\u{10c81}"), + (0x10CC2, "\u{10c82}"), + (0x10CC3, "\u{10c83}"), + (0x10CC4, "\u{10c84}"), + (0x10CC5, "\u{10c85}"), + (0x10CC6, "\u{10c86}"), + (0x10CC7, "\u{10c87}"), + (0x10CC8, "\u{10c88}"), + (0x10CC9, "\u{10c89}"), + (0x10CCA, "\u{10c8a}"), + (0x10CCB, "\u{10c8b}"), + (0x10CCC, "\u{10c8c}"), + (0x10CCD, "\u{10c8d}"), + (0x10CCE, "\u{10c8e}"), + (0x10CCF, "\u{10c8f}"), + (0x10CD0, "\u{10c90}"), + (0x10CD1, "\u{10c91}"), + (0x10CD2, "\u{10c92}"), + (0x10CD3, "\u{10c93}"), + (0x10CD4, "\u{10c94}"), + (0x10CD5, "\u{10c95}"), + (0x10CD6, "\u{10c96}"), + (0x10CD7, "\u{10c97}"), + (0x10CD8, "\u{10c98}"), + (0x10CD9, "\u{10c99}"), + (0x10CDA, "\u{10c9a}"), + (0x10CDB, "\u{10c9b}"), + (0x10CDC, "\u{10c9c}"), + (0x10CDD, "\u{10c9d}"), + (0x10CDE, "\u{10c9e}"), + (0x10CDF, "\u{10c9f}"), + (0x10CE0, "\u{10ca0}"), + (0x10CE1, "\u{10ca1}"), + (0x10CE2, "\u{10ca2}"), + (0x10CE3, "\u{10ca3}"), + (0x10CE4, "\u{10ca4}"), + (0x10CE5, "\u{10ca5}"), + (0x10CE6, "\u{10ca6}"), + (0x10CE7, "\u{10ca7}"), + (0x10CE8, "\u{10ca8}"), + (0x10CE9, "\u{10ca9}"), + (0x10CEA, "\u{10caa}"), + (0x10CEB, "\u{10cab}"), + (0x10CEC, "\u{10cac}"), + (0x10CED, "\u{10cad}"), + (0x10CEE, "\u{10cae}"), + (0x10CEF, "\u{10caf}"), + (0x10CF0, "\u{10cb0}"), + (0x10CF1, "\u{10cb1}"), + (0x10CF2, "\u{10cb2}"), + (0x10D70, "\u{10d50}"), + (0x10D71, "\u{10d51}"), + (0x10D72, "\u{10d52}"), + (0x10D73, "\u{10d53}"), + (0x10D74, "\u{10d54}"), + (0x10D75, "\u{10d55}"), + (0x10D76, "\u{10d56}"), + (0x10D77, "\u{10d57}"), + (0x10D78, "\u{10d58}"), + (0x10D79, "\u{10d59}"), + (0x10D7A, "\u{10d5a}"), + (0x10D7B, "\u{10d5b}"), + (0x10D7C, "\u{10d5c}"), + (0x10D7D, "\u{10d5d}"), + (0x10D7E, "\u{10d5e}"), + (0x10D7F, "\u{10d5f}"), + (0x10D80, "\u{10d60}"), + (0x10D81, "\u{10d61}"), + (0x10D82, "\u{10d62}"), + (0x10D83, "\u{10d63}"), + (0x10D84, "\u{10d64}"), + (0x10D85, "\u{10d65}"), + (0x118C0, "\u{118a0}"), + (0x118C1, "\u{118a1}"), + (0x118C2, "\u{118a2}"), + (0x118C3, "\u{118a3}"), + (0x118C4, "\u{118a4}"), + (0x118C5, "\u{118a5}"), + (0x118C6, "\u{118a6}"), + (0x118C7, "\u{118a7}"), + (0x118C8, "\u{118a8}"), + (0x118C9, "\u{118a9}"), + (0x118CA, "\u{118aa}"), + (0x118CB, "\u{118ab}"), + (0x118CC, "\u{118ac}"), + (0x118CD, "\u{118ad}"), + (0x118CE, "\u{118ae}"), + (0x118CF, "\u{118af}"), + (0x118D0, "\u{118b0}"), + (0x118D1, "\u{118b1}"), + (0x118D2, "\u{118b2}"), + (0x118D3, "\u{118b3}"), + (0x118D4, "\u{118b4}"), + (0x118D5, "\u{118b5}"), + (0x118D6, "\u{118b6}"), + (0x118D7, "\u{118b7}"), + (0x118D8, "\u{118b8}"), + (0x118D9, "\u{118b9}"), + (0x118DA, "\u{118ba}"), + (0x118DB, "\u{118bb}"), + (0x118DC, "\u{118bc}"), + (0x118DD, "\u{118bd}"), + (0x118DE, "\u{118be}"), + (0x118DF, "\u{118bf}"), + (0x16E60, "\u{16e40}"), + (0x16E61, "\u{16e41}"), + (0x16E62, "\u{16e42}"), + (0x16E63, "\u{16e43}"), + (0x16E64, "\u{16e44}"), + (0x16E65, "\u{16e45}"), + (0x16E66, "\u{16e46}"), + (0x16E67, "\u{16e47}"), + (0x16E68, "\u{16e48}"), + (0x16E69, "\u{16e49}"), + (0x16E6A, "\u{16e4a}"), + (0x16E6B, "\u{16e4b}"), + (0x16E6C, "\u{16e4c}"), + (0x16E6D, "\u{16e4d}"), + (0x16E6E, "\u{16e4e}"), + (0x16E6F, "\u{16e4f}"), + (0x16E70, "\u{16e50}"), + (0x16E71, "\u{16e51}"), + (0x16E72, "\u{16e52}"), + (0x16E73, "\u{16e53}"), + (0x16E74, "\u{16e54}"), + (0x16E75, "\u{16e55}"), + (0x16E76, "\u{16e56}"), + (0x16E77, "\u{16e57}"), + (0x16E78, "\u{16e58}"), + (0x16E79, "\u{16e59}"), + (0x16E7A, "\u{16e5a}"), + (0x16E7B, "\u{16e5b}"), + (0x16E7C, "\u{16e5c}"), + (0x16E7D, "\u{16e5d}"), + (0x16E7E, "\u{16e5e}"), + (0x16E7F, "\u{16e5f}"), + (0x1E922, "\u{1e900}"), + (0x1E923, "\u{1e901}"), + (0x1E924, "\u{1e902}"), + (0x1E925, "\u{1e903}"), + (0x1E926, "\u{1e904}"), + (0x1E927, "\u{1e905}"), + (0x1E928, "\u{1e906}"), + (0x1E929, "\u{1e907}"), + (0x1E92A, "\u{1e908}"), + (0x1E92B, "\u{1e909}"), + (0x1E92C, "\u{1e90a}"), + (0x1E92D, "\u{1e90b}"), + (0x1E92E, "\u{1e90c}"), + (0x1E92F, "\u{1e90d}"), + (0x1E930, "\u{1e90e}"), + (0x1E931, "\u{1e90f}"), + (0x1E932, "\u{1e910}"), + (0x1E933, "\u{1e911}"), + (0x1E934, "\u{1e912}"), + (0x1E935, "\u{1e913}"), + (0x1E936, "\u{1e914}"), + (0x1E937, "\u{1e915}"), + (0x1E938, "\u{1e916}"), + (0x1E939, "\u{1e917}"), + (0x1E93A, "\u{1e918}"), + (0x1E93B, "\u{1e919}"), + (0x1E93C, "\u{1e91a}"), + (0x1E93D, "\u{1e91b}"), + (0x1E93E, "\u{1e91c}"), + (0x1E93F, "\u{1e91d}"), + (0x1E940, "\u{1e91e}"), + (0x1E941, "\u{1e91f}"), + (0x1E942, "\u{1e920}"), + (0x1E943, "\u{1e921}"), +] diff --git a/crates/sqlbuild-python/src/bindings/_helpers/boundary/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/boundary/mod.rs index 0a21932dc9..8069dd72e2 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/boundary/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/boundary/mod.rs @@ -2,3 +2,4 @@ mod allocator; pub(crate) mod config_values; pub(crate) mod oracles; pub(crate) mod panics; +pub(crate) mod python_runtime; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs b/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs new file mode 100644 index 0000000000..46cea30266 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs @@ -0,0 +1,22 @@ +//! Record the running Python's string semantics for native text helpers. + +use pyo3::prelude::{PyAnyMethods, PyResult, Python}; +use sqlbuild_core::text::main::active_python_text::set_active_python_text; +use sqlbuild_core::text::main::python_text::python_text; + +/// Select the running Python's `str` semantics; an unsupported Python fails at discovery instead. +pub(crate) fn record_python_text(python: Python<'_>) -> PyResult<()> { + let version: (u8, u8) = python + .import("sys")? + .getattr("version_info")? + .extract::<(u8, u8, u8, String, u8)>() + .map(|(major, minor, ..)| (major, minor))?; + let unicode_version: String = python + .import("unicodedata")? + .getattr("unidata_version")? + .extract()?; + if let Some(text) = python_text(version, &unicode_version) { + set_active_python_text(text); + } + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs index ba39f3fa24..ce2bce62cc 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs @@ -5,12 +5,15 @@ use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::contracts::main::evaluate_model_contracts::evaluate_model_contracts; use sqlbuild_analysis::contracts::main::promotion_conflicts::promotion_conflicts; use sqlbuild_analysis::contracts::models::{ - ContractDeferral, ContractDiagnostic, ContractModel, ContractOutcome, ContractRequest, + ContractDiagnostic, ContractModel, ContractRequest, ContractSchema, ContractSeverity, DeclaredColumn, DeclaredColumnFamily, DynamicContractProof, InferredOutputColumn, PromotionConflict, PromotionModel, PromotionRequest, }; +use sqlbuild_analysis::type_system::models::TypeNormalizationError; + use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::type_system::normalization::normalization_error; use crate::bindings::types::CompilerDetach; /// `(name, type, not_null, declared_in_named_schema)`. @@ -40,27 +43,29 @@ type DiagnosticRow = ( Option<(String, String)>, String, ); -/// `(deferral_kind, diagnostics)`. -type OutcomeRow = (Option<&'static str>, Vec); /// `(name, contract, materialized, incremental_mode)`. type PromotionModelRow = (String, Option, Option, Option); -/// Evaluate every model's column contract; deferred models carry their deferral kind. +/// Evaluate every model's column contract, raising Python's type normalization error. #[pyfunction] fn evaluate_native_model_contracts( py: Python<'_>, request: (String, bool, Vec), -) -> PyResult> { +) -> PyResult>> { let (dialect, implicit_column_contracts, models) = request; let request = ContractRequest { dialect, implicit_column_contracts, models: models.into_iter().map(contract_model).collect(), }; - let outcomes: Vec = py + let outcomes: Result>, TypeNormalizationError> = py .compiler_detach(|| Ok(evaluate_model_contracts(&request))) .map_err(compiler_error)?; - Ok(outcomes.into_iter().map(outcome_row).collect()) + Ok(outcomes + .map_err(|error| normalization_error(py, &error))? + .into_iter() + .map(|diagnostics| diagnostics.into_iter().map(diagnostic_row).collect()) + .collect()) } /// The K011 conflicts of enforced contracts under immediate table promotion. @@ -160,14 +165,6 @@ fn contract_schema( } } -fn outcome_row(outcome: ContractOutcome) -> OutcomeRow { - let (deferral, diagnostics) = outcome.into_parts(); - ( - deferral.map(ContractDeferral::as_str), - diagnostics.into_iter().map(diagnostic_row).collect(), - ) -} - fn diagnostic_row(diagnostic: ContractDiagnostic) -> DiagnosticRow { let (declared_index, output_column) = diagnostic.location.into_parts(); ( diff --git a/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs b/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs index c4c2d802e5..a5c038937a 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs @@ -1,39 +1,63 @@ -//! Native type normalization for the preview type system. +//! Native type normalization. -use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult}; +use pyo3::exceptions::PyValueError; +use pyo3::prelude::{Bound, PyAnyMethods, PyErr, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::type_system::main::normalize_type::normalize_type; -use sqlbuild_analysis::type_system::models::{NormalizedType, TypeNormalization}; +use sqlbuild_analysis::type_system::models::{ + NormalizedType, TypeNormalization, TypeNormalizationError, +}; use crate::bindings::_helpers::boundary::panics::compiler_guard; -/// `(name, family, precision, scale, length)` of one normalized type. -type NormalizedRow = (String, &'static str, Option, Option, Option); +/// `(name, family, precision, scale, length)` of one normalized type; the integers are the +/// base-10 text of Python `int`s of any size. +type NormalizedRow = ( + String, + &'static str, + Option, + Option, + Option, +); -/// Normalize one type: `(normalized row, Polyglot parse error)`, or None to defer to Python. +/// Normalize one type: `(normalized row, Polyglot parse error)`, or Python's exception. #[pyfunction] #[pyo3(name = "normalize_type")] fn normalize_type_binding( + python: Python<'_>, type_sql: &str, dialect: &str, -) -> PyResult)>> { - compiler_guard(|| { - Ok(normalize_type(type_sql, dialect).map( - |TypeNormalization { - normalized, - parse_error, - }| (normalized_row(normalized), parse_error), - )) - }) +) -> PyResult<(NormalizedRow, Option)> { + let normalization: Result = + compiler_guard(|| Ok(normalize_type(type_sql, dialect)))?; + match normalization { + Ok(TypeNormalization { + normalized, + parse_error, + }) => Ok((normalized_row(normalized), parse_error)), + Err(error) => Err(normalization_error(python, &error)), + } +} + +/// The exception the Polyglot wheel raises for the same failure. +pub(crate) fn normalization_error(python: Python<'_>, error: &TypeNormalizationError) -> PyErr { + match error { + TypeNormalizationError::UnknownDialect(_) => PyValueError::new_err(error.message()), + TypeNormalizationError::Generation(_) => python + .import("polyglot_sql") + .and_then(|module| module.getattr("PolyglotError")) + .and_then(|class| class.call1((error.message(),))) + .map_or_else(|import_error| import_error, PyErr::from_value), + } } fn normalized_row(normalized: NormalizedType) -> NormalizedRow { ( normalized.normalized_name, normalized.family.as_str(), - normalized.precision, - normalized.scale, - normalized.length, + normalized.precision.map(|value| value.as_str().to_owned()), + normalized.scale.map(|value| value.as_str().to_owned()), + normalized.length.map(|value| value.as_str().to_owned()), ) } diff --git a/crates/sqlbuild-python/src/bindings/main/register.rs b/crates/sqlbuild-python/src/bindings/main/register.rs index 0e0d1ed5c9..dacbe3588f 100644 --- a/crates/sqlbuild-python/src/bindings/main/register.rs +++ b/crates/sqlbuild-python/src/bindings/main/register.rs @@ -12,6 +12,7 @@ use crate::bindings::models; #[pymodule] pub fn _native(module: &Bound<'_, PyModule>) -> PyResult<()> { + boundary::python_runtime::record_python_text(module.py())?; rules::evaluation::register(module)?; rules::lint::register(module)?; rules::skills::register(module)?; diff --git a/scripts/python_char_table/_helpers/table.py b/scripts/python_char_table/_helpers/table.py index 43802eda6b..2400356677 100644 --- a/scripts/python_char_table/_helpers/table.py +++ b/scripts/python_char_table/_helpers/table.py @@ -1,4 +1,4 @@ -"""Range computation and Rust rendering of Python's `str.isalnum()` and `str.isalpha()` tables.""" +"""Range and mapping computation, and Rust rendering, of Python's `str` character tables.""" from __future__ import annotations @@ -7,6 +7,7 @@ from scripts.python_char_table.constants import ( MAX_CODE_POINT, + SURROGATE_RANGE, TABLE_FILE_PREFIX, TABLE_FILE_SUFFIX, TABLE_ROOT, @@ -21,6 +22,43 @@ def table_path(*, method: str) -> Path: return directory / f"{TABLE_FILE_PREFIX}{version}{TABLE_FILE_SUFFIX}" +def mapping_path(*, method: str) -> Path: + """Return the Rust file holding one case mapping for the running Python's Unicode version.""" + + version: str = unicodedata.unidata_version.replace(".", "_") + return TABLE_ROOT / f"{method}_mappings" / f"{TABLE_FILE_PREFIX}{version}{TABLE_FILE_SUFFIX}" + + +def character_mappings(*, method: str) -> list[tuple[int, str]]: + """Return every character the `str` method changes, with what it becomes, in order.""" + + mappings: list[tuple[int, str]] = [] + for code_point in range(MAX_CODE_POINT + 1): + if code_point in SURROGATE_RANGE: + continue + character: str = chr(code_point) + mapped: str = getattr(character, method)() + if mapped != character: + mappings.append((code_point, mapped)) + return mappings + + +def render_mapping(*, method: str, mappings: list[tuple[int, str]]) -> str: + """Render the Rust expression of one case mapping table, which `case_tables.rs` includes.""" + + lines: list[str] = [ + "// Generated by `scripts/generate_python_char_table.py`; do not edit.", + f"// Every character `str.{method}()` changes in Unicode {unicodedata.unidata_version}, " + "in code point order.", + "&[", + ] + lines.extend( + f' (0x{code_point:04X}, "{_rust_escaped(mapped)}"),' for code_point, mapped in mappings + ) + lines.append("]") + return "\n".join(lines) + "\n" + + def character_ranges(*, method: str) -> list[tuple[int, int]]: """Return the inclusive code point ranges where the `str` method is true.""" @@ -50,3 +88,7 @@ def render_table(*, method: str, ranges: list[tuple[int, int]]) -> str: lines.extend(f" (0x{start:04X}, 0x{end:04X})," for start, end in ranges) lines.append("]") return "\n".join(lines) + "\n" + + +def _rust_escaped(text: str) -> str: + return "".join(f"\\u{{{ord(character):x}}}" for character in text) diff --git a/scripts/python_char_table/constants.py b/scripts/python_char_table/constants.py index 84818e990a..4aaa691e43 100644 --- a/scripts/python_char_table/constants.py +++ b/scripts/python_char_table/constants.py @@ -7,3 +7,5 @@ TABLE_FILE_SUFFIX: str = ".in" MAX_CODE_POINT: int = 0x10FFFF TABLE_METHODS: tuple[str, ...] = ("isalnum", "isalpha", "isdecimal") +MAPPING_METHODS: tuple[str, ...] = ("casefold", "upper") +SURROGATE_RANGE: range = range(0xD800, 0xE000) diff --git a/scripts/python_char_table/main/generate.py b/scripts/python_char_table/main/generate.py index 41a0e34a00..fa40fb8ae9 100644 --- a/scripts/python_char_table/main/generate.py +++ b/scripts/python_char_table/main/generate.py @@ -6,8 +6,15 @@ import unicodedata from pathlib import Path -from scripts.python_char_table._helpers.table import character_ranges, render_table, table_path -from scripts.python_char_table.constants import TABLE_METHODS +from scripts.python_char_table._helpers.table import ( + character_mappings, + character_ranges, + mapping_path, + render_mapping, + render_table, + table_path, +) +from scripts.python_char_table.constants import MAPPING_METHODS, TABLE_METHODS def generate_python_char_table() -> int: @@ -20,4 +27,12 @@ def generate_python_char_table() -> int: render_table(method=method, ranges=character_ranges(method=method)), encoding="utf-8" ) print(f"wrote {path} (Unicode {unicodedata.unidata_version})", file=sys.stderr) + for method in MAPPING_METHODS: + path = mapping_path(method=method) + path.parent.mkdir(parents=True, exist_ok=True) + _ = path.write_text( + render_mapping(method=method, mappings=character_mappings(method=method)), + encoding="utf-8", + ) + print(f"wrote {path} (Unicode {unicodedata.unidata_version})", file=sys.stderr) return 0 diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 5dc6994151..e38df82be4 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -358,7 +358,7 @@ def digest_files(paths: list[str]) -> list[str | None]: ... # Native analysis: type system. def normalize_type( type_sql: str, dialect: str -) -> tuple[tuple[str, str, int | None, int | None, int | None], str | None] | None: ... +) -> tuple[tuple[str, str, str | None, str | None, str | None], str | None]: ... # Native analysis: model analysis session. type _AnalysisDiagnosticRow = tuple[str, str, int | None, int | None, int | None, int | None, str] @@ -626,20 +626,17 @@ def evaluate_native_model_contracts( ], /, ) -> list[ - tuple[ - str | None, - list[ - tuple[ - str, - bool, - str, - str | None, - int | None, - str | None, - tuple[str, str] | None, - str, - ] - ], + list[ + tuple[ + str, + bool, + str, + str | None, + int | None, + str | None, + tuple[str, str] | None, + str, + ] ] ]: ... def native_promotion_conflicts( diff --git a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py index 15409a4ceb..c49ea238f5 100644 --- a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py +++ b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py @@ -2,71 +2,18 @@ from __future__ import annotations -import logging -import re from functools import lru_cache -from typing import Any -from sqlbuild.adapter.contract.constants import ( - BOOLEAN_TYPE_NAMES, - DATE_TYPE_NAME, - DATETIME_TYPE_NAME, - DECIMAL_TYPE_NAMES, - FLOAT_TYPE_NAMES, - INTEGER_TYPE_NAMES, - POLYGLOT_CUSTOM_TYPE_NAME, - STRING_TYPE_NAMES, - TIMESTAMP_TYPE_NAMES, - TIMESTAMP_TYPE_TOKEN, -) from sqlbuild.adapter.contract.models import NormalizedType from sqlbuild.adapter.contract.types import TypeDialect, TypeFamily -from sqlbuild.adapter.type_system.constants import ( - TYPE_NORMALIZATION_LOGGER_NAME, - TYPE_PARSE_FALLBACK_MESSAGE, -) from sqlbuild.adapter.type_system.main._native_normalize_type import normalize_native_type -from sqlbuild.adapters.bigquery.constants import ( - BIGNUMERIC_TYPE_NAME, - CUSTOM_NORMALIZATION_TYPE_NAMES, - FLOAT_WIRE_TYPE_NAME, - INTEGER_PARSE_TYPE_NAMES, -) -from sqlbuild.adapters.snowflake.constants import ( - DEFAULT_TEXT_LENGTH, - INTEGER_PRECISION, - INTEGER_SCALE, - NORMALIZED_LTZ_INPUT_TYPE_NAME, - NORMALIZED_NTZ_INPUT_TYPE_NAMES, - NORMALIZED_TZ_INPUT_TYPE_NAME, - TEXT_TYPE_NAME, - UNBOUNDED_TEXT_TYPE_NAMES, -) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite -from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot -from sqlbuild.diagnostics.main.log_debug_event import log_debug_event - -_DEBUG_LOGGER: logging.Logger = logging.getLogger(TYPE_NORMALIZATION_LOGGER_NAME) @lru_cache(maxsize=1024) def normalize_type(*, type_sql: str, dialect: TypeDialect | str | None) -> NormalizedType: """Normalize one warehouse type string into a semantic comparison shape.""" - native_normalized: NormalizedType | None = normalize_native_type( - type_sql=type_sql, dialect=dialect - ) - if native_normalized is not None: - return native_normalized - report_native_fallback(site=NativeFallbackSite.TYPE_NORMALIZATION) - polyglot_normalized: NormalizedType | None = _normalize_with_polyglot( - type_sql=type_sql, - dialect=dialect, - ) - if polyglot_normalized is not None: - return polyglot_normalized - return _normalize_with_fallback(type_sql=type_sql, dialect=dialect) + return normalize_native_type(type_sql=type_sql, dialect=dialect) def normalize_numeric_family(*, type_sql: str, dialect: TypeDialect | str | None) -> str | None: @@ -85,313 +32,3 @@ def types_equal(*, left: str, right: str, dialect: TypeDialect | str | None) -> type_sql=right, dialect=dialect, ) - - -def _normalize_with_polyglot( - *, type_sql: str, dialect: TypeDialect | str | None -) -> NormalizedType | None: - polyglot_module: Any = import_polyglot() - try: - parsed: Any = polyglot_module.parse_data_type(type_sql, dialect=dialect or "generic") - except polyglot_module.PolyglotError as error: - log_debug_event( - logger=_DEBUG_LOGGER, - message=TYPE_PARSE_FALLBACK_MESSAGE, - type_sql=type_sql, - dialect=str(dialect), - sqlbuild_error=str(error), - ) - return None - return _normalized_from_parsed_type(parsed=parsed, dialect=dialect) - - -def _normalized_from_parsed_type( - *, parsed: Any, dialect: TypeDialect | str | None -) -> NormalizedType: - normalized_dialect: TypeDialect | None = _coerce_type_dialect(dialect) - normalized_name: str = parsed.sql(dialect=dialect or "generic").upper().replace(" ", "") - args: dict[str, Any] = dict(getattr(parsed, "args", {}) or {}) - dtype_name: str = _polyglot_type_name(args=args) - params: list[int] = _polyglot_type_params(args=args) - precision_and_scale_count: int = 2 - - if normalized_dialect == TypeDialect.BIGQUERY and dtype_name in INTEGER_PARSE_TYPE_NAMES: - normalized_name = "INT64" - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.INTEGER) - if normalized_dialect == TypeDialect.BIGQUERY and dtype_name == BIGNUMERIC_TYPE_NAME: - return NormalizedType(normalized_name="BIGNUMERIC", family=TypeFamily.DECIMAL) - if normalized_dialect == TypeDialect.BIGQUERY and dtype_name == FLOAT_WIRE_TYPE_NAME: - return NormalizedType(normalized_name="FLOAT64", family=TypeFamily.FLOAT) - if dtype_name == POLYGLOT_CUSTOM_TYPE_NAME: - raw_name: str = str(args.get("name", normalized_name)).upper().replace(" ", "") - if normalized_dialect == TypeDialect.SNOWFLAKE and raw_name.startswith("NUMBER"): - decimal_name: str = raw_name.replace("NUMBER", "DECIMAL", 1) - base_type, params = _split_type_and_params(decimal_name) - precision: int | None = params[0] if len(params) >= 1 else None - scale: int | None = params[1] if len(params) >= precision_and_scale_count else None - if precision is None: - precision = INTEGER_PRECISION - scale = INTEGER_SCALE - decimal_name = f"DECIMAL({precision},{scale})" - return NormalizedType( - normalized_name=decimal_name, - family=TypeFamily.DECIMAL, - precision=precision, - scale=scale, - ) - return _normalize_with_fallback(type_sql=raw_name, dialect=dialect) - if dtype_name in INTEGER_TYPE_NAMES: - if normalized_dialect == TypeDialect.SNOWFLAKE: - return NormalizedType( - normalized_name=f"DECIMAL({INTEGER_PRECISION},{INTEGER_SCALE})", - family=TypeFamily.DECIMAL, - precision=INTEGER_PRECISION, - scale=INTEGER_SCALE, - ) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.INTEGER) - if dtype_name in DECIMAL_TYPE_NAMES: - precision: int | None = params[0] if len(params) >= 1 else None - scale: int | None = params[1] if len(params) >= precision_and_scale_count else None - if normalized_dialect == TypeDialect.SNOWFLAKE and precision is None: - precision = INTEGER_PRECISION - scale = INTEGER_SCALE - normalized_name = f"DECIMAL({precision},{scale})" - return NormalizedType( - normalized_name=normalized_name, - family=TypeFamily.DECIMAL, - precision=precision, - scale=scale, - ) - if dtype_name in FLOAT_TYPE_NAMES: - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.FLOAT) - if dtype_name in STRING_TYPE_NAMES: - length: int | None = params[0] if len(params) >= 1 else None - if ( - normalized_dialect == TypeDialect.SNOWFLAKE - and _base_type_name(normalized_name) in UNBOUNDED_TEXT_TYPE_NAMES - ): - length = length or DEFAULT_TEXT_LENGTH - normalized_name = f"VARCHAR({length})" - return NormalizedType( - normalized_name=normalized_name, - family=TypeFamily.STRING, - length=length, - ) - if dtype_name in BOOLEAN_TYPE_NAMES: - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.BOOLEAN) - if dtype_name in TIMESTAMP_TYPE_NAMES: - normalized_name = _timestamp_normalized_name( - normalized_name=normalized_name, - dialect=normalized_dialect, - ) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.TIMESTAMP) - if dtype_name == DATE_TYPE_NAME: - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.DATE) - if dtype_name == DATETIME_TYPE_NAME: - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.DATETIME) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.OTHER) - - -def _normalize_with_fallback(*, type_sql: str, dialect: TypeDialect | str | None) -> NormalizedType: - normalized_dialect: TypeDialect | None = _coerce_type_dialect(dialect) - normalized: str = type_sql.upper().strip() - normalized = re.sub(r"\s+", "", normalized) - base_type, params = _split_type_and_params(normalized) - precision_and_scale_count: int = 2 - - if base_type in INTEGER_TYPE_NAMES: - if normalized_dialect == TypeDialect.SNOWFLAKE: - return NormalizedType( - normalized_name=f"DECIMAL({INTEGER_PRECISION},{INTEGER_SCALE})", - family=TypeFamily.DECIMAL, - precision=INTEGER_PRECISION, - scale=INTEGER_SCALE, - ) - normalized_name: str = _fallback_integer_normalized_name( - base_type=base_type, - dialect=normalized_dialect, - ) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.INTEGER) - if base_type in DECIMAL_TYPE_NAMES: - precision: int | None = params[0] if len(params) >= 1 else None - scale: int | None = params[1] if len(params) >= precision_and_scale_count else None - if normalized_dialect == TypeDialect.SNOWFLAKE and precision is None: - precision = INTEGER_PRECISION - scale = INTEGER_SCALE - normalized_name = _fallback_decimal_normalized_name( - base_type=base_type, - dialect=normalized_dialect, - ) - if precision is not None and scale is not None: - normalized_name = f"{normalized_name}({precision},{scale})" - elif precision is not None: - normalized_name = f"{normalized_name}({precision})" - return NormalizedType( - normalized_name=normalized_name, - family=TypeFamily.DECIMAL, - precision=precision, - scale=scale, - ) - if base_type in FLOAT_TYPE_NAMES: - normalized_name = _fallback_float_normalized_name( - base_type=base_type, - dialect=normalized_dialect, - ) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.FLOAT) - if base_type in STRING_TYPE_NAMES: - length: int | None = params[0] if len(params) >= 1 else None - normalized_name = _fallback_string_normalized_name( - base_type=base_type, - dialect=normalized_dialect, - ) - normalized_name = normalized_name if length is None else f"{normalized_name}({length})" - if normalized_dialect == TypeDialect.SNOWFLAKE and base_type in UNBOUNDED_TEXT_TYPE_NAMES: - length = length or DEFAULT_TEXT_LENGTH - normalized_name = f"VARCHAR({length})" - return NormalizedType( - normalized_name=normalized_name, - family=TypeFamily.STRING, - length=length, - ) - if base_type in BOOLEAN_TYPE_NAMES: - normalized_name = _fallback_boolean_normalized_name( - base_type=base_type, - dialect=normalized_dialect, - ) - return NormalizedType(normalized_name=normalized_name, family=TypeFamily.BOOLEAN) - if TIMESTAMP_TYPE_TOKEN in base_type: - normalized_timestamp_name: str = _timestamp_normalized_name( - normalized_name=base_type, - dialect=normalized_dialect, - ) - return NormalizedType( - normalized_name=normalized_timestamp_name, - family=TypeFamily.TIMESTAMP, - ) - if base_type == DATE_TYPE_NAME: - return NormalizedType(normalized_name=base_type, family=TypeFamily.DATE) - if base_type == DATETIME_TYPE_NAME: - return NormalizedType(normalized_name=base_type, family=TypeFamily.DATETIME) - return NormalizedType(normalized_name=normalized, family=TypeFamily.OTHER) - - -def _split_type_and_params(type_sql: str) -> tuple[str, list[int]]: - match: re.Match[str] | None = re.match(r"^([A-Z0-9_]+)(?:\(([^)]*)\))?$", type_sql) - if match is None: - return type_sql, [] - raw_params: str | None = match.group(2) - if raw_params is None or not raw_params: - return match.group(1), [] - params: list[int] = [] - raw_part: str - for raw_part in raw_params.split(","): - try: - params.append(int(raw_part.strip())) - except ValueError: - continue - return match.group(1), params - - -def _base_type_name(type_sql: str) -> str: - return type_sql.split("(", maxsplit=1)[0] - - -def _fallback_integer_normalized_name(*, base_type: str, dialect: TypeDialect | None) -> str: - if dialect == TypeDialect.BIGQUERY: - return "INT64" - return base_type - - -def _fallback_decimal_normalized_name(*, base_type: str, dialect: TypeDialect | None) -> str: - if dialect == TypeDialect.BIGQUERY: - if base_type == BIGNUMERIC_TYPE_NAME: - return "BIGNUMERIC" - return "NUMERIC" - if dialect == TypeDialect.SNOWFLAKE: - return "DECIMAL" - return base_type - - -def _fallback_float_normalized_name(*, base_type: str, dialect: TypeDialect | None) -> str: - if dialect == TypeDialect.BIGQUERY: - return "FLOAT64" - return base_type - - -def _fallback_string_normalized_name(*, base_type: str, dialect: TypeDialect | None) -> str: - if dialect == TypeDialect.BIGQUERY: - return "STRING" - if dialect == TypeDialect.SNOWFLAKE and base_type == TEXT_TYPE_NAME: - return "VARCHAR" - return base_type - - -def _fallback_boolean_normalized_name(*, base_type: str, dialect: TypeDialect | None) -> str: - if dialect == TypeDialect.BIGQUERY: - return "BOOL" - return base_type - - -def _timestamp_normalized_name(*, normalized_name: str, dialect: TypeDialect | None) -> str: - if dialect != TypeDialect.SNOWFLAKE: - return normalized_name - compact: str = normalized_name.replace("_", "") - if compact in NORMALIZED_NTZ_INPUT_TYPE_NAMES: - return "TIMESTAMP_NTZ" - if compact == NORMALIZED_LTZ_INPUT_TYPE_NAME: - return "TIMESTAMP_LTZ" - if compact == NORMALIZED_TZ_INPUT_TYPE_NAME: - return "TIMESTAMP_TZ" - return normalized_name - - -def _polyglot_type_name(*, args: dict[str, Any]) -> str: - data_type: str = str(args.get("data_type", "")).upper() - polyglot_name_map: dict[str, str] = { - "BIG_INT": "BIGINT", - "SMALL_INT": "SMALLINT", - "TINY_INT": "TINYINT", - "VAR_CHAR": "VARCHAR", - "TEXT": "TEXT", - "INT": "INT", - "BOOLEAN": "BOOLEAN", - "DECIMAL": "DECIMAL", - "DOUBLE": "DOUBLE", - "FLOAT": "FLOAT", - "TIMESTAMP": "TIMESTAMP", - "DATE": "DATE", - "DATETIME": "DATETIME", - } - mapped_name: str | None = polyglot_name_map.get(data_type) - if mapped_name is not None: - return mapped_name - if data_type == POLYGLOT_CUSTOM_TYPE_NAME: - name: str = str(args.get("name", "")).upper().replace(" ", "") - if name in CUSTOM_NORMALIZATION_TYPE_NAMES: - return name - return data_type - - -def _polyglot_type_params(*, args: dict[str, Any]) -> list[int]: - params: list[int] = [] - key: str - for key in ("precision", "scale", "length"): - value: Any = args.get(key) - if value is None: - continue - try: - params.append(int(value)) - except (TypeError, ValueError): - continue - return params - - -def _coerce_type_dialect(dialect: TypeDialect | str | None) -> TypeDialect | None: - if isinstance(dialect, TypeDialect): - return dialect - if dialect is None: - return None - try: - return TypeDialect(dialect) - except ValueError: - return None diff --git a/src/sqlbuild/adapter/type_system/main/_native_normalize_type.py b/src/sqlbuild/adapter/type_system/main/_native_normalize_type.py index 6d9a9ceb50..77f1c5d714 100644 --- a/src/sqlbuild/adapter/type_system/main/_native_normalize_type.py +++ b/src/sqlbuild/adapter/type_system/main/_native_normalize_type.py @@ -1,4 +1,4 @@ -"""Native type normalization for the preview compiler engine.""" +"""Native type normalization.""" from __future__ import annotations @@ -14,19 +14,12 @@ from sqlbuild.diagnostics.main.log_debug_event import log_debug_event -def normalize_native_type( - *, type_sql: str, dialect: TypeDialect | str | None -) -> NormalizedType | None: - """Return the native normalization, or None where Python must normalize the type.""" +def normalize_native_type(*, type_sql: str, dialect: TypeDialect | str | None) -> NormalizedType: + """Return the native normalization of one type, raising as Polyglot does.""" - if not isinstance(type_sql, str) or not isinstance(dialect, str | None): - return None - native: tuple[tuple[str, str, int | None, int | None, int | None], str | None] | None = ( - _native.normalize_type(type_sql, str(dialect or "generic")) + (normalized_name, family, precision, scale, length), parse_error = _native.normalize_type( + type_sql, str(dialect or "generic") ) - if native is None: - return None - (normalized_name, family, precision, scale, length), parse_error = native if parse_error is not None: log_debug_event( logger=logging.getLogger(TYPE_NORMALIZATION_LOGGER_NAME), @@ -38,7 +31,11 @@ def normalize_native_type( return NormalizedType( normalized_name=normalized_name, family=TypeFamily(family), - precision=precision, - scale=scale, - length=length, + precision=_python_int(precision), + scale=_python_int(scale), + length=_python_int(length), ) + + +def _python_int(value: str | None) -> int | None: + return None if value is None else int(value) diff --git a/src/sqlbuild/compiler/contracts/_helpers/columns.py b/src/sqlbuild/compiler/contracts/_helpers/columns.py deleted file mode 100644 index d718379e33..0000000000 --- a/src/sqlbuild/compiler/contracts/_helpers/columns.py +++ /dev/null @@ -1,421 +0,0 @@ -"""Model column contract validation.""" - -from __future__ import annotations - -from functools import lru_cache -from pathlib import Path - -from sqlbuild.adapter.contract.types import TypeDialect -from sqlbuild.adapter.type_system.main.types_equal import types_equal -from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.compile.models import ( - CompiledModel, - CompilerDiagnostic, - DynamicColumnContractProof, - DynamicColumnFamilyProof, - InferredColumn, - RelatedLocation, -) -from sqlbuild.compiler.compile.types import ( - CompiledResourceType, - DiagnosticPhase, - DiagnosticSeverity, -) -from sqlbuild.compiler.discovery.main._model_output_column_locations import ( - extract_model_output_column_locations, -) -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.planner.types import ContractPolicy -from sqlbuild.spec.contracts.models import ( - SchemaColumn, - SchemaDynamicColumnFamily, - SourceLocation, -) - -_MISSING_COLUMN_CODE: str = "K001" -_TYPE_MISMATCH_CODE: str = "K002" -_UNKNOWN_TYPE_CODE: str = "K003" -_NULLABILITY_MISMATCH_CODE: str = "K004" -_EXTRA_COLUMN_CODE: str = "K005" -_MISSING_DECLARATIONS_CODE: str = "K006" -_DYNAMIC_OUTPUT_NOT_PROVEN_CODE: str = "K011" - - -def collect_model_column_contract_diagnostics( - *, - model: CompiledModel, - validate_declared_shape: bool, - dialect: TypeDialect | str | None = None, -) -> tuple[CompilerDiagnostic, ...]: - """Collect diagnostics for one compiled model's declared column contract.""" - - contract_enforced: bool = model.config.values.get("contract") == ContractPolicy.ENFORCED - if model.schema_entry is None or not ( - model.schema_entry.columns or model.schema_entry.dynamic_columns - ): - if contract_enforced: - return (_missing_declarations_diagnostic(model),) - return () - diagnostics: list[CompilerDiagnostic] = [] - diagnostics.extend(_dynamic_column_diagnostics(model=model, dialect=dialect)) - dynamic_output_unproven: bool = any( - diagnostic.code == _DYNAMIC_OUTPUT_NOT_PROVEN_CODE for diagnostic in diagnostics - ) - if model.inferred_columns is None: - return tuple(diagnostics) - - inferred_by_name: dict[str, InferredColumn] = { - column.name: column for column in model.inferred_columns - } - if contract_enforced: - diagnostics.extend(_extra_column_diagnostics(model=model)) - declared_column: SchemaColumn - for declared_column in model.schema_entry.columns: - inferred_column: InferredColumn | None = inferred_by_name.get(declared_column.name) - if inferred_column is None: - if validate_declared_shape and not model.fast_lineage_has_star: - diagnostics.append( - _missing_column_diagnostic( - model=model, - column=declared_column, - contract_enforced=contract_enforced, - ) - ) - continue - if validate_declared_shape: - diagnostics.extend( - _nullability_diagnostics( - model=model, - declared_column=declared_column, - inferred_column=inferred_column, - ) - ) - if declared_column.type is None: - continue - type_enforcement: bool = model.schema_entry is not None and bool( - model.schema_entry.type_enforcement - ) - if not validate_declared_shape and not type_enforcement: - continue - if dynamic_output_unproven and inferred_column.type is None: - continue - diagnostics.extend( - _type_diagnostics( - model=model, - declared_column=declared_column, - inferred_column=inferred_column, - dialect=dialect, - contract_enforced=contract_enforced, - ) - ) - return tuple(diagnostics) - - -def _dynamic_column_diagnostics( - *, model: CompiledModel, dialect: TypeDialect | str | None -) -> tuple[CompilerDiagnostic, ...]: - if model.schema_entry is None or not model.schema_entry.dynamic_columns: - return () - proof: DynamicColumnContractProof | None = model.dynamic_column_contract - if proof is None or not proof.output_proven: - failure_reason: str = ( - proof.failure_reason - if proof is not None and proof.failure_reason - else "no compiler evidence" - ) - return ( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_DYNAMIC_OUTPUT_NOT_PROVEN_CODE, - message=( - f"model '{model.name}' dynamic column contract is not proven: {failure_reason}" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - path=model.relative_path, - help=( - "project one supported runtime-dynamic pivot through a wildcard output " - "boundary " - "and declare every generated family" - ), - ), - ) - proof_by_name: dict[str, DynamicColumnFamilyProof] = { - family.name.casefold(): family for family in proof.families - } - diagnostics: list[CompilerDiagnostic] = [] - family: SchemaDynamicColumnFamily - for family in model.schema_entry.dynamic_columns: - family_proof: DynamicColumnFamilyProof | None = proof_by_name.get(family.name.casefold()) - inferred_type: str | None = family_proof.inferred_type if family_proof is not None else None - if inferred_type is None: - diagnostics.append( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.WARNING, - code=_UNKNOWN_TYPE_CODE, - message=( - f"dynamic column family '{family.name}' type could not be proven against " - f"declared {family.type}" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - path=model.relative_path, - help=( - "establish an authoritative type on the pivot value column or CAST the " - "type-preserving aggregate input explicitly" - ), - ) - ) - elif not types_equal(left=family.type, right=inferred_type, dialect=dialect): - diagnostics.append( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_TYPE_MISMATCH_CODE, - message=( - f"dynamic column family '{family.name}' inferred as {inferred_type} " - f"but declared type is {family.type}" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - path=model.relative_path, - help="correct the family type or the pivot aggregate input type", - ) - ) - return tuple(diagnostics) - - -def _missing_declarations_diagnostic(model: CompiledModel) -> CompilerDiagnostic: - return CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_MISSING_DECLARATIONS_CODE, - message=f"model '{model.name}' has contract enforced but declares no columns", - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - path=model.relative_path, - help=( - "add MODEL(columns (...)), declare a proven MODEL(dynamic_columns (...)) family, " - "or set contract none for this model" - ), - ) - - -def _extra_column_diagnostics(model: CompiledModel) -> tuple[CompilerDiagnostic, ...]: - if model.schema_entry is None or model.inferred_columns is None: - return () - declared_names: set[str] = {column.name for column in model.schema_entry.columns} - diagnostics: list[CompilerDiagnostic] = [] - inferred_column: InferredColumn - for inferred_column in model.inferred_columns: - if inferred_column.name in declared_names: - continue - diagnostics.append( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_EXTRA_COLUMN_CODE, - message=( - f"column '{inferred_column.name}' is not declared in enforced contract " - f"for model '{model.name}'" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - column_name=inferred_column.name, - path=model.relative_path, - location=_output_column_location(model=model, column_name=inferred_column.name), - help=( - "add the column to the named SCHEMA or remove it from the SELECT list" - if model.schema_entry.model_schema is not None - else "add the column to MODEL(columns) or remove it from the SELECT list" - ), - ) - ) - return tuple(diagnostics) - - -def _nullability_diagnostics( - *, - model: CompiledModel, - declared_column: SchemaColumn, - inferred_column: InferredColumn, -) -> tuple[CompilerDiagnostic, ...]: - if not _declares_not_null(declared_column): - return () - if inferred_column.nullability != InferredNullability.NULLABLE: - return () - return ( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_NULLABILITY_MISMATCH_CODE, - message=f"column '{declared_column.name}' is declared non-null but may be nullable", - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - column_name=declared_column.name, - path=model.relative_path, - location=declared_column.location, - related_locations=_output_related_locations( - model=model, - column_name=declared_column.name, - message="output expression proven nullable", - ), - help="use COALESCE, filter nulls explicitly, or remove the non-null contract", - ), - ) - - -def _declares_not_null(column: SchemaColumn) -> bool: - return column.nullable is False - - -def _missing_column_diagnostic( - *, model: CompiledModel, column: SchemaColumn, contract_enforced: bool -) -> CompilerDiagnostic: - contract_label: str = "enforced contract column" if contract_enforced else "declared column" - declared_in_named_schema: bool = ( - model.schema_entry is not None - and model.schema_entry.model_schema is not None - and column.location is not None - and column.location.path != model.relative_path - ) - declaration_label: str = ( - "the named SCHEMA declaration" if declared_in_named_schema else "MODEL(columns (...))" - ) - help_text: str = ( - f"add {column.name} to the SELECT list or correct {declaration_label}; " - "validation is active because this model declares contract enforced" - if contract_enforced - else ( - f"add {column.name} to the SELECT list or correct/remove {declaration_label}; " - f"{declaration_label} is validated using static SQL analysis because " - 'settings.column_contract_mode is "implicit" (the default). If this project ' - "intentionally uses columns only for metadata and audits, set [settings] " - 'column_contract_mode = "explicit"; models with contract enforced remain validated' - ) - ) - return CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR, - code=_MISSING_COLUMN_CODE, - message=( - f"{contract_label} '{column.name}' was not found in statically inferred output " - f"for model '{model.name}'" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - column_name=column.name, - path=model.relative_path, - location=column.location, - help=help_text, - ) - - -def _type_diagnostics( - *, - model: CompiledModel, - declared_column: SchemaColumn, - inferred_column: InferredColumn, - dialect: TypeDialect | str | None, - contract_enforced: bool, -) -> tuple[CompilerDiagnostic, ...]: - declared_type: str | None = declared_column.type - if declared_type is None: - raise CompileInputError( - f"model '{model.name}' column '{declared_column.name}' reached type validation " - "without a declared type", - code="P003", - ) - type_enforcement: bool = model.schema_entry is not None and bool( - model.schema_entry.type_enforcement - ) - if inferred_column.type is None: - if not type_enforcement or inferred_column.name in model.unchecked_output_columns: - return () - return ( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.WARNING, - code=_UNKNOWN_TYPE_CODE, - message=( - f"column '{declared_column.name}' type could not be proven against " - f"declared {declared_type}" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - column_name=declared_column.name, - path=model.relative_path, - location=declared_column.location, - related_locations=_output_related_locations( - model=model, - column_name=declared_column.name, - message="output expression with unproven type", - ), - help="add an explicit CAST if this declared type should be checked statically", - ), - ) - - if types_equal(left=declared_type, right=inferred_column.type, dialect=dialect): - return () - - return ( - CompilerDiagnostic( - phase=DiagnosticPhase.CONTRACT, - severity=DiagnosticSeverity.ERROR - if type_enforcement or contract_enforced - else DiagnosticSeverity.WARNING, - code=_TYPE_MISMATCH_CODE, - message=( - f"column '{declared_column.name}' inferred as {inferred_column.type} " - f"but declared type is {declared_type}" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=model.name, - column_name=declared_column.name, - path=model.relative_path, - location=declared_column.location, - related_locations=_output_related_locations( - model=model, - column_name=declared_column.name, - message=f"inferred {inferred_column.type}", - ), - help="change the declared type or cast the expression explicitly", - ), - ) - - -def _output_related_locations( - *, model: CompiledModel, column_name: str, message: str -) -> tuple[RelatedLocation, ...]: - location: SourceLocation | None = _output_column_location(model=model, column_name=column_name) - if location is None: - return () - return (RelatedLocation(label="output", location=location, message=message),) - - -def _output_column_location(*, model: CompiledModel, column_name: str) -> SourceLocation | None: - location: SourceLocation | None = model.output_column_locations.get(column_name) - if location is not None or not model.authored_sql: - return location - return dict( - _lazy_output_column_locations( - contents=model.authored_sql, - relative_path=model.relative_path, - extract_implicit_alias_columns=model.extract_implicit_alias_columns, - ) - ).get(column_name) - - -@lru_cache(maxsize=256) -def _lazy_output_column_locations( - *, contents: str, relative_path: Path, extract_implicit_alias_columns: bool -) -> tuple[tuple[str, SourceLocation], ...]: - return tuple( - extract_model_output_column_locations( - contents=contents, - relative_path=relative_path, - extract_implicit_alias_columns=extract_implicit_alias_columns, - ).items() - ) diff --git a/src/sqlbuild/compiler/contracts/_helpers/evaluation.py b/src/sqlbuild/compiler/contracts/_helpers/evaluation.py deleted file mode 100644 index 794bf59167..0000000000 --- a/src/sqlbuild/compiler/contracts/_helpers/evaluation.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Which models need contract evaluation, and Python's evaluation of one model.""" - -from __future__ import annotations - -from sqlbuild.adapter.contract.types import TypeDialect -from sqlbuild.compiler.compile.models import CompiledModel, CompilerDiagnostic -from sqlbuild.compiler.contracts._helpers.columns import collect_model_column_contract_diagnostics -from sqlbuild.compiler.planner.types import ContractPolicy -from sqlbuild.spec.contracts.types import ColumnContractMode - - -def requires_contract_evaluation(*, model: CompiledModel, mode: ColumnContractMode) -> bool: - """Whether the model declares a shape to validate or enforces declared types.""" - - if declared_shape_validation_is_active(model=model, mode=mode): - return True - return model.schema_entry is not None and bool(model.schema_entry.type_enforcement) - - -def declared_shape_validation_is_active(*, model: CompiledModel, mode: ColumnContractMode) -> bool: - """Whether the model's declared columns are validated against its inferred output.""" - - contract: object = model.config.values.get("contract") - if contract == ContractPolicy.ENFORCED: - return True - if contract == ContractPolicy.NONE: - return False - return mode == ColumnContractMode.IMPLICIT and bool( - model.schema_entry is not None and model.schema_entry.columns - ) - - -def python_model_contract_diagnostics( - *, model: CompiledModel, mode: ColumnContractMode, dialect: TypeDialect | str | None -) -> tuple[CompilerDiagnostic, ...]: - """Python's contract diagnostics for one model that requires evaluation.""" - - return collect_model_column_contract_diagnostics( - model=model, - validate_declared_shape=declared_shape_validation_is_active(model=model, mode=mode), - dialect=dialect, - ) diff --git a/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py b/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py index d708f68acf..b296483fde 100644 --- a/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py +++ b/src/sqlbuild/compiler/contracts/_helpers/native_contracts.py @@ -16,16 +16,10 @@ DiagnosticPhase, DiagnosticSeverity, ) -from sqlbuild.compiler.contracts._helpers.columns import ( - _output_column_location, - _output_related_locations, +from sqlbuild.compiler.contracts._helpers.output_locations import ( + output_column_location, + output_related_locations, ) -from sqlbuild.compiler.contracts._helpers.evaluation import ( - python_model_contract_diagnostics, - requires_contract_evaluation, -) -from sqlbuild.compiler.contracts._helpers.native_deferrals import record_contract_deferral -from sqlbuild.compiler.contracts.constants import NATIVE_CONTRACTS_DEFERRAL_SITE from sqlbuild.compiler.contracts.models import ContractValidationResult from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.spec.contracts.models import SchemaColumn, SchemaModelEntry, SourceLocation @@ -51,27 +45,18 @@ def native_model_contracts( *, project: CompiledProject, dialect: TypeDialect | str | None ) -> ContractValidationResult: - """Return `evaluate_model_contracts`'s result, with deferred models evaluated by Python.""" + """Return every model's column contract diagnostics, in model order.""" mode: ColumnContractMode = project.settings.column_contract_mode - outcomes: list[tuple[str | None, list[_DiagnosticRow]]] = ( - _native.evaluate_native_model_contracts( - ( - str(dialect or "generic"), - mode == ColumnContractMode.IMPLICIT, - [_model_row(model) for model in project.models], - ) + outcomes: list[list[_DiagnosticRow]] = _native.evaluate_native_model_contracts( + ( + str(dialect or "generic"), + mode == ColumnContractMode.IMPLICIT, + [_model_row(model) for model in project.models], ) ) diagnostics: list[CompilerDiagnostic] = [] - for model, (deferral, rows) in zip(project.models, outcomes, strict=True): - if deferral is not None: - record_contract_deferral(kind=deferral, site=NATIVE_CONTRACTS_DEFERRAL_SITE) - if requires_contract_evaluation(model=model, mode=mode): - diagnostics.extend( - python_model_contract_diagnostics(model=model, mode=mode, dialect=dialect) - ) - continue + for model, rows in zip(project.models, outcomes, strict=True): diagnostics.extend(_diagnostic(model=model, row=row) for row in rows) return ContractValidationResult(diagnostics=tuple(diagnostics)) @@ -151,7 +136,7 @@ def _location( if declared_index is not None and model.schema_entry is not None: return model.schema_entry.columns[declared_index].location if output_column is not None: - return _output_column_location(model=model, column_name=output_column) + return output_column_location(model=model, column_name=output_column) return None @@ -161,4 +146,4 @@ def _related_locations( if related is None: return () column_name, message = related - return _output_related_locations(model=model, column_name=column_name, message=message) + return output_related_locations(model=model, column_name=column_name, message=message) diff --git a/src/sqlbuild/compiler/contracts/_helpers/native_deferrals.py b/src/sqlbuild/compiler/contracts/_helpers/native_deferrals.py deleted file mode 100644 index 18d245c041..0000000000 --- a/src/sqlbuild/compiler/contracts/_helpers/native_deferrals.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Debug-only record of contract work the native engine hands back to Python.""" - -from __future__ import annotations - -import json -import os -from pathlib import Path - -from sqlbuild.compiler.lineage.constants import ANALYSIS_DEFERRAL_RECORD_PREFIX -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR - - -def record_contract_deferral(*, kind: str, site: str) -> None: - """Append one `{"kind", "site"}` line when the analysis record directory is set.""" - - directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) - if not directory: - return - target: Path = Path(directory) - target.mkdir(parents=True, exist_ok=True) - record: str = json.dumps({"kind": kind, "site": site}) - with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: - _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/contracts/_helpers/output_locations.py b/src/sqlbuild/compiler/contracts/_helpers/output_locations.py new file mode 100644 index 0000000000..70cf20d658 --- /dev/null +++ b/src/sqlbuild/compiler/contracts/_helpers/output_locations.py @@ -0,0 +1,51 @@ +"""Source locations of a model's output columns, for contract diagnostics.""" + +from __future__ import annotations + +from functools import lru_cache +from pathlib import Path + +from sqlbuild.compiler.compile.models import CompiledModel, RelatedLocation +from sqlbuild.compiler.discovery.main._model_output_column_locations import ( + extract_model_output_column_locations, +) +from sqlbuild.spec.contracts.models import SourceLocation + + +def output_related_locations( + *, model: CompiledModel, column_name: str, message: str +) -> tuple[RelatedLocation, ...]: + """The output column's location as a related location, when the model has one.""" + + location: SourceLocation | None = output_column_location(model=model, column_name=column_name) + if location is None: + return () + return (RelatedLocation(label="output", location=location, message=message),) + + +def output_column_location(*, model: CompiledModel, column_name: str) -> SourceLocation | None: + """Where the model's SQL produces one output column.""" + + location: SourceLocation | None = model.output_column_locations.get(column_name) + if location is not None or not model.authored_sql: + return location + return dict( + _lazy_output_column_locations( + contents=model.authored_sql, + relative_path=model.relative_path, + extract_implicit_alias_columns=model.extract_implicit_alias_columns, + ) + ).get(column_name) + + +@lru_cache(maxsize=256) +def _lazy_output_column_locations( + *, contents: str, relative_path: Path, extract_implicit_alias_columns: bool +) -> tuple[tuple[str, SourceLocation], ...]: + return tuple( + extract_model_output_column_locations( + contents=contents, + relative_path=relative_path, + extract_implicit_alias_columns=extract_implicit_alias_columns, + ).items() + ) diff --git a/src/sqlbuild/compiler/contracts/constants.py b/src/sqlbuild/compiler/contracts/constants.py index a96e4cdb03..beb870aca5 100644 --- a/src/sqlbuild/compiler/contracts/constants.py +++ b/src/sqlbuild/compiler/contracts/constants.py @@ -1,4 +1,3 @@ """Model contract decision constants.""" NOT_NULL_AUDIT_NAME: str = "not_null" -NATIVE_CONTRACTS_DEFERRAL_SITE: str = "contracts/columns.py" diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index 67a894c002..a79f8116d7 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -40,5 +40,4 @@ class NativeFallbackSite(StrEnum): MACRO_BRIDGE_UNAVAILABLE = "macro_calls.bridge_unavailable" MACRO_CALL_MOCKED = "macro_calls.mocked_evaluation" PROJECT_ASSEMBLY = "project_assembly.assembly" - TYPE_NORMALIZATION = "type_system.normalization" SQL_TEST_ASSEMBLY = "sql_test_glue.assembly" diff --git a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py index 1d687c65c5..a35add8b98 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py @@ -1,47 +1,59 @@ -"""Test case types for native type normalization parity.""" +"""Test case types for native type normalization.""" from __future__ import annotations from dataclasses import dataclass +from sqlbuild.adapter.contract.models import NormalizedType + @dataclass(frozen=True) -class GeneratedTypeParityTestCase: +class GeneratedTypeTestCase: """Seeded generated type strings normalized under every dialect.""" description: str seed: int count: int - expected_minimum_native: int + expected_minimum_normalized: int expected_minimum_parse_errors: int - expected_minimum_deferred: int + expected_unknown_dialect_errors: int @dataclass(frozen=True) -class TypeParityTestCase: - """One type string normalized natively and by Python under every dialect.""" +class TypeNormalizationTestCase: + """One type string under one dialect, its normalization and whether a parse error is logged.""" description: str type_sql: str - expected_native_dialects: frozenset[str | None] + dialect: str | None + expected_type: NormalizedType + expected_parse_error_logged: bool + + +@dataclass(frozen=True) +class UnknownDialectTypeTestCase: + """A dialect Polyglot does not know, and the error Python's wheel raised for it.""" + + description: str + dialect: str + expected_error: str @dataclass(frozen=True) class DeepTypeTestCase: - """A type deeper than the wheel can parse, which only native sees.""" + """A type deeper than the Polyglot wheel itself could parse.""" description: str type_sql: str - expected_native: None + expected_family: str @dataclass(frozen=True) class PublicNativeTypeTestCase: - """Types normalized through the public entry point under the preview engine.""" + """Types normalized through the public entry point under each engine.""" description: str engine: str dialect: str type_strings: tuple[str, ...] expected_native_calls: tuple[tuple[str, str], ...] - expected_answered: int diff --git a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py index fa4546e97d..25337fbea2 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py @@ -1,4 +1,4 @@ -"""Generated type strings and Python-versus-native normalization outcomes.""" +"""Generated type strings and their native normalization outcomes.""" from __future__ import annotations @@ -10,7 +10,6 @@ from itertools import product from sqlbuild.adapter.contract.models import NormalizedType -from sqlbuild.adapter.type_system._helpers import type_normalization from sqlbuild.adapter.type_system.constants import TYPE_NORMALIZATION_LOGGER_NAME from sqlbuild.adapter.type_system.main._native_normalize_type import normalize_native_type @@ -121,20 +120,12 @@ @dataclass(frozen=True) class TypeOutcome: - """A normalized type, Python's error, or None where native defers, and the errors logged.""" - - normalized: NormalizedType | str | None - logged_errors: tuple[tuple[str, object, object, object], ...] - - -@dataclass(frozen=True) -class TypeParity: - """One type string and dialect, normalized by Python and natively.""" + """One type string and dialect: its normalization or Python's error, and the errors logged.""" type_sql: str dialect: str | None - python: TypeOutcome - native: TypeOutcome + normalized: NormalizedType | str + logged_errors: tuple[tuple[str, object, object, object], ...] def generated_type(*, rng: random.Random, depth: int = 0) -> str: @@ -147,40 +138,39 @@ def generated_type(*, rng: random.Random, depth: int = 0) -> str: return template.format(*children, scalar=_generated_scalar(rng=rng)) -def type_parities(*, type_strings: list[str]) -> list[TypeParity]: - """Normalize every type string under every dialect with Python and natively.""" +def type_outcomes(*, type_strings: list[str]) -> list[TypeOutcome]: + """Normalize every type string under every dialect.""" - parities: list[TypeParity] = [] - for type_sql, dialect in product(type_strings, DIALECTS): - parities.append(type_parity(type_sql=type_sql, dialect=dialect)) - return parities + return [ + type_outcome(type_sql=type_sql, dialect=dialect) + for type_sql, dialect in product(type_strings, DIALECTS) + ] -def type_parity(*, type_sql: str, dialect: str | None) -> TypeParity: - """Normalize one type with the Python fallback native defers to and natively.""" +def type_outcome(*, type_sql: str, dialect: str | None) -> TypeOutcome: + """Normalize one type natively, recording the parse errors it logs.""" - with _logged_errors() as python_errors: - python: NormalizedType | str = _python_normalized(type_sql=type_sql, dialect=dialect) - with _logged_errors() as native_errors: - native: NormalizedType | None = normalize_native_type(type_sql=type_sql, dialect=dialect) - return TypeParity( - type_sql=type_sql, - dialect=dialect, - python=TypeOutcome(normalized=python, logged_errors=tuple(python_errors)), - native=TypeOutcome(normalized=native, logged_errors=tuple(native_errors)), + with _logged_errors() as errors: + normalized: NormalizedType | str + try: + normalized = normalize_native_type(type_sql=type_sql, dialect=dialect) + except ValueError as error: + normalized = f"{type(error).__name__}: {error}" + return TypeOutcome( + type_sql=type_sql, dialect=dialect, normalized=normalized, logged_errors=tuple(errors) ) -def is_native(parity: TypeParity) -> bool: - """Return whether the native type system answered.""" +def is_normalized(outcome: TypeOutcome) -> bool: + """Return whether the type normalized rather than raising.""" - return parity.native.normalized is not None + return isinstance(outcome.normalized, NormalizedType) -def logged_parse_error(parity: TypeParity) -> bool: - """Return whether Python logged a Polyglot parse failure for this type.""" +def logged_parse_error(outcome: TypeOutcome) -> bool: + """Return whether a Polyglot parse failure was logged for this type.""" - return bool(parity.python.logged_errors) + return bool(outcome.logged_errors) def _generated_scalar(*, rng: random.Random) -> str: @@ -190,17 +180,6 @@ def _generated_scalar(*, rng: random.Random) -> str: ) -def _python_normalized(*, type_sql: str, dialect: str | None) -> NormalizedType | str: - try: - return type_normalization._normalize_with_polyglot( # noqa: SLF001 - type_sql=type_sql, dialect=dialect - ) or type_normalization._normalize_with_fallback( # noqa: SLF001 - type_sql=type_sql, dialect=dialect - ) - except ValueError as error: - return f"{type(error).__name__}: {error}" - - @contextmanager def _logged_errors() -> Iterator[list[tuple[str, object, object, object]]]: records: list[tuple[str, object, object, object]] = [] diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py new file mode 100644 index 0000000000..fbc1006264 --- /dev/null +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py @@ -0,0 +1,310 @@ +"""Native type normalization: generated types, explicit shapes and Python's errors.""" + +from __future__ import annotations + +import random +from collections.abc import Callable + +import pytest + +import sqlbuild._native as native_module +from sqlbuild.adapter.contract.models import NormalizedType +from sqlbuild.adapter.contract.types import TypeFamily +from sqlbuild.adapter.type_system._helpers.type_normalization import ( + normalize_type as cached_normalize_type, +) +from sqlbuild.adapter.type_system.main.normalize_type import normalize_type +from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR +from tests.integration.src.sqlbuild.adapter.type_system._test_types import ( + DeepTypeTestCase, + GeneratedTypeTestCase, + PublicNativeTypeTestCase, + TypeNormalizationTestCase, + UnknownDialectTypeTestCase, +) +from tests.integration.src.sqlbuild.adapter.type_system.helpers import ( + TypeOutcome, + generated_type, + is_normalized, + logged_parse_error, + type_outcome, + type_outcomes, +) + + +@pytest.mark.parametrize( + "test_case", + [ + GeneratedTypeTestCase( + description="seeded scalar, parameterized, nested, padded and invalid types", + seed=20261008, + count=1500, + expected_minimum_normalized=16000, + expected_minimum_parse_errors=5000, + expected_unknown_dialect_errors=1500, + ), + ], + ids=lambda case: case.description, +) +def test_given_generated_types_when_normalizing_then_every_known_dialect_answers( + test_case: GeneratedTypeTestCase, +) -> None: + rng: random.Random = random.Random(test_case.seed) + type_strings: list[str] = [generated_type(rng=rng) for _ in range(test_case.count)] + outcomes: list[TypeOutcome] = type_outcomes(type_strings=type_strings) + normalized: list[TypeOutcome] = list(filter(is_normalized, outcomes)) + + assert ( + len(normalized) >= test_case.expected_minimum_normalized, + sum(map(logged_parse_error, normalized)) >= test_case.expected_minimum_parse_errors, + len(outcomes) - len(normalized), + ) == (True, True, test_case.expected_unknown_dialect_errors), test_case.description + + +@pytest.mark.parametrize( + "test_case", + [ + TypeNormalizationTestCase( + description="integer aliases under BigQuery", + type_sql="INTEGER", + dialect="bigquery", + expected_type=NormalizedType(normalized_name="INT64", family=TypeFamily.INTEGER), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="snowflake number precision and scale defaults", + type_sql="NUMBER", + dialect="snowflake", + expected_type=NormalizedType( + normalized_name="DECIMAL(38,0)", family=TypeFamily.DECIMAL, precision=38, scale=0 + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="spaced decimal parameters", + type_sql=" decimal( 10 , 2 ) ", + dialect="duckdb", + expected_type=NormalizedType( + normalized_name="DECIMAL(10,2)", family=TypeFamily.DECIMAL, precision=10, scale=2 + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="unbounded snowflake text", + type_sql="TEXT", + dialect="snowflake", + expected_type=NormalizedType( + normalized_name="VARCHAR(16777216)", family=TypeFamily.STRING, length=16777216 + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="nested array of structs", + type_sql="ARRAY>>", + dialect="bigquery", + expected_type=NormalizedType( + normalized_name="ARRAY>>", + family=TypeFamily.OTHER, + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="map of varchar to decimal", + type_sql="MAP(VARCHAR, DECIMAL(18, 4))", + dialect="duckdb", + expected_type=NormalizedType( + normalized_name="MAP(TEXT,DECIMAL(18,4))", family=TypeFamily.OTHER + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="quoted custom type", + type_sql='"Order Status"', + dialect="postgres", + expected_type=NormalizedType(normalized_name="ORDERSTATUS", family=TypeFamily.OTHER), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="timestamp aliases", + type_sql="timestamp_ntz(9)", + dialect="snowflake", + expected_type=NormalizedType( + normalized_name="TIMESTAMP_NTZ", family=TypeFamily.TIMESTAMP + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="invalid type falls back after a logged parse error", + type_sql="order status", + dialect="generic", + expected_type=NormalizedType(normalized_name="ORDERSTATUS", family=TypeFamily.OTHER), + expected_parse_error_logged=True, + ), + TypeNormalizationTestCase( + description="parameters beyond i64 stay Python integers", + type_sql="VARCHAR(99999999999999999999)", + dialect="snowflake", + expected_type=NormalizedType( + normalized_name="VARCHAR(99999999999999999999)", + family=TypeFamily.STRING, + length=99999999999999999999, + ), + expected_parse_error_logged=True, + ), + TypeNormalizationTestCase( + description="array suffixes past the old bracket cap", + type_sql="INT" + "[]" * 33 + "", + dialect="duckdb", + expected_type=NormalizedType( + normalized_name="INT" + "[]" * 33, family=TypeFamily.OTHER + ), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="non-ASCII text upper-cases as Python does", + type_sql="caf\u00e9", + dialect="generic", + expected_type=NormalizedType(normalized_name="CAF\u00c9", family=TypeFamily.OTHER), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="non-ASCII text folds sharp s as Python does", + type_sql="stra\u00dfe", + dialect="snowflake", + expected_type=NormalizedType(normalized_name="STRASSE", family=TypeFamily.OTHER), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="Unicode decimal digits are Python integers", + type_sql="VARCHAR(\u0663)", + dialect="snowflake", + expected_type=NormalizedType( + normalized_name="VARCHAR(3)", family=TypeFamily.STRING, length=3 + ), + expected_parse_error_logged=True, + ), + TypeNormalizationTestCase( + description="a dialect outside the old native build", + type_sql="INT", + dialect="mysql", + expected_type=NormalizedType(normalized_name="INT", family=TypeFamily.INTEGER), + expected_parse_error_logged=False, + ), + TypeNormalizationTestCase( + description="a dialect-specific type outside the old native build", + type_sql="UInt8", + dialect="clickhouse", + expected_type=NormalizedType(normalized_name="UINT8", family=TypeFamily.OTHER), + expected_parse_error_logged=False, + ), + ], + ids=lambda case: case.description, +) +def test_given_type_when_normalizing_then_python_shape_is_returned( + test_case: TypeNormalizationTestCase, +) -> None: + outcome: TypeOutcome = type_outcome(type_sql=test_case.type_sql, dialect=test_case.dialect) + + assert (outcome.normalized, logged_parse_error(outcome)) == ( + test_case.expected_type, + test_case.expected_parse_error_logged, + ), test_case.description + + +@pytest.mark.parametrize( + "test_case", + [ + UnknownDialectTypeTestCase( + description="a dialect name Polyglot does not know", + dialect="motherduck", + expected_error="ValueError: Unknown dialect: motherduck", + ), + ], + ids=lambda case: case.description, +) +def test_given_unknown_dialect_when_normalizing_then_python_error_is_raised( + test_case: UnknownDialectTypeTestCase, +) -> None: + outcome: TypeOutcome = type_outcome(type_sql="INT", dialect=test_case.dialect) + + assert outcome.normalized == test_case.expected_error, test_case.description + + +@pytest.mark.parametrize( + "test_case", + [ + DeepTypeTestCase( + description="100,000 array suffixes, past where the wheel itself overflows", + type_sql="INT" + "[]" * 100_000, + expected_family=TypeFamily.OTHER, + ), + ], + ids=lambda case: case.description, +) +def test_given_type_past_the_wheel_depth_when_normalizing_then_it_is_normalized( + test_case: DeepTypeTestCase, +) -> None: + outcome: TypeOutcome = type_outcome(type_sql=test_case.type_sql, dialect="generic") + + assert isinstance(outcome.normalized, NormalizedType) + assert outcome.normalized.family == test_case.expected_family + + +@pytest.mark.parametrize( + "test_case", + [ + PublicNativeTypeTestCase( + description="the public entry point asks native first under the preview engine", + engine="native-preview", + dialect="snowflake", + type_strings=("NUMBER(38, 0)", "VARCHAR", "TIMESTAMP_NTZ(9)"), + expected_native_calls=( + ("NUMBER(38, 0)", "snowflake"), + ("VARCHAR", "snowflake"), + ("TIMESTAMP_NTZ(9)", "snowflake"), + ), + ), + PublicNativeTypeTestCase( + description="the public entry point asks native first under the default engine", + engine="native", + dialect="snowflake", + type_strings=("NUMBER(38, 0)", "VARCHAR", "TIMESTAMP_NTZ(9)"), + expected_native_calls=( + ("NUMBER(38, 0)", "snowflake"), + ("VARCHAR", "snowflake"), + ("TIMESTAMP_NTZ(9)", "snowflake"), + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_native_engine_when_normalizing_publicly_then_native_answers( + test_case: PublicNativeTypeTestCase, monkeypatch: pytest.MonkeyPatch +) -> None: + calls: list[tuple[str, str]] = [] + answers: list[object] = [] + native_normalize: Callable[[str, str], object] = native_module.normalize_type + + def spy(type_sql: str, dialect: str) -> object: + calls.append((type_sql, dialect)) + answers.append(native_normalize(type_sql, dialect)) + return answers[-1] + + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, test_case.engine) + monkeypatch.setattr(native_module, "normalize_type", spy) + cached_normalize_type.cache_clear() + normalized: list[NormalizedType] = [ + normalize_type(type_sql=type_sql, dialect=test_case.dialect) + for type_sql in test_case.type_strings + ] + cached_normalize_type.cache_clear() + + assert (tuple(calls), len(answers), len(normalized)) == ( + test_case.expected_native_calls, + len(test_case.type_strings), + len(test_case.type_strings), + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py deleted file mode 100644 index 0219ed19b6..0000000000 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py +++ /dev/null @@ -1,238 +0,0 @@ -"""Native type normalization matches Python for every type it answers, or defers.""" - -from __future__ import annotations - -import random -from collections.abc import Callable - -import pytest - -import sqlbuild._native as native_module -from sqlbuild.adapter.contract.models import NormalizedType -from sqlbuild.adapter.type_system._helpers.type_normalization import ( - normalize_type as cached_normalize_type, -) -from sqlbuild.adapter.type_system.main._native_normalize_type import normalize_native_type -from sqlbuild.adapter.type_system.main.normalize_type import normalize_type -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR -from tests.integration.src.sqlbuild.adapter.type_system._test_types import ( - DeepTypeTestCase, - GeneratedTypeParityTestCase, - PublicNativeTypeTestCase, - TypeParityTestCase, -) -from tests.integration.src.sqlbuild.adapter.type_system.helpers import ( - TypeParity, - generated_type, - is_native, - logged_parse_error, - type_parities, -) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches - -_NATIVE_DIALECTS: frozenset[str | None] = frozenset( - {None, "generic", "bigquery", "snowflake", "duckdb", "databricks", "postgres", "tsql"} - | {"postgresql", "DuckDB"} -) - - -@pytest.mark.parametrize( - "test_case", - [ - GeneratedTypeParityTestCase( - description="seeded scalar, parameterized, nested, padded and invalid types", - seed=20261008, - count=1500, - expected_minimum_native=12000, - expected_minimum_parse_errors=5000, - expected_minimum_deferred=3000, - ), - ], - ids=lambda case: case.description, -) -def test_given_generated_types_when_normalizing_natively_then_python_normalization_matches( - test_case: GeneratedTypeParityTestCase, -) -> None: - rng: random.Random = random.Random(test_case.seed) - type_strings: list[str] = [generated_type(rng=rng) for _ in range(test_case.count)] - parities: list[TypeParity] = type_parities(type_strings=type_strings) - - answered: list[TypeParity] = list(filter(is_native, parities)) - assert ( - mismatches( - inputs=[(parity.type_sql, parity.dialect) for parity in answered], - expected=[parity.python for parity in answered], - actual=[parity.native for parity in answered], - ), - len(answered) >= test_case.expected_minimum_native, - sum(map(logged_parse_error, answered)) >= test_case.expected_minimum_parse_errors, - len(parities) - len(answered) >= test_case.expected_minimum_deferred, - ) == ([], True, True, True), test_case.description - - -@pytest.mark.parametrize( - "test_case", - [ - TypeParityTestCase( - description="integer aliases", - type_sql="INTEGER", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="snowflake number precision and scale defaults", - type_sql="NUMBER", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="spaced decimal parameters", - type_sql=" decimal( 10 , 2 ) ", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="unbounded text", - type_sql="TEXT", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="nested array of structs", - type_sql="ARRAY>>", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="map of varchar to decimal", - type_sql="MAP(VARCHAR, DECIMAL(18, 4))", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="quoted custom type", - type_sql='"Order Status"', - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="timestamp aliases", - type_sql="timestamp_ntz(9)", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="invalid type falls back after a logged parse error", - type_sql="order status", - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="parameters Python keeps as large integers defer", - type_sql="VARCHAR(99999999999999999999)", - expected_native_dialects=frozenset(), - ), - TypeParityTestCase( - description="array suffixes at the bracket cap", - type_sql="INT" + "[]" * 32, - expected_native_dialects=_NATIVE_DIALECTS, - ), - TypeParityTestCase( - description="array suffixes past the bracket cap defer", - type_sql="INT" + "[]" * 33, - expected_native_dialects=frozenset(), - ), - TypeParityTestCase( - description="non-ASCII text defers", - type_sql="caf\u00e9", - expected_native_dialects=frozenset(), - ), - ], - ids=lambda case: case.description, -) -def test_given_type_when_normalizing_under_each_dialect_then_native_answers_match_python( - test_case: TypeParityTestCase, -) -> None: - parities: list[TypeParity] = type_parities(type_strings=[test_case.type_sql]) - - answered: list[TypeParity] = list(filter(is_native, parities)) - assert ( - mismatches( - inputs=[parity.dialect for parity in answered], - expected=[parity.python for parity in answered], - actual=[parity.native for parity in answered], - ), - frozenset(parity.dialect for parity in answered), - ) == ([], test_case.expected_native_dialects), test_case.description - - -@pytest.mark.parametrize( - "test_case", - [ - DeepTypeTestCase( - description="100,000 array suffixes, past where the wheel itself overflows", - type_sql="INT" + "[]" * 100_000, - expected_native=None, - ), - ], - ids=lambda case: case.description, -) -def test_given_type_past_the_wheel_depth_when_normalizing_natively_then_it_defers( - test_case: DeepTypeTestCase, -) -> None: - assert normalize_native_type(type_sql=test_case.type_sql, dialect="generic") is ( - test_case.expected_native - ) - - -@pytest.mark.parametrize( - "test_case", - [ - PublicNativeTypeTestCase( - description="the public entry point asks native first under the preview engine", - engine="native-preview", - dialect="snowflake", - type_strings=("NUMBER(38, 0)", "VARCHAR", "TIMESTAMP_NTZ(9)"), - expected_native_calls=( - ("NUMBER(38, 0)", "snowflake"), - ("VARCHAR", "snowflake"), - ("TIMESTAMP_NTZ(9)", "snowflake"), - ), - expected_answered=3, - ), - PublicNativeTypeTestCase( - description="the public entry point asks native first under the default engine", - engine="native", - dialect="snowflake", - type_strings=("NUMBER(38, 0)", "VARCHAR", "TIMESTAMP_NTZ(9)"), - expected_native_calls=( - ("NUMBER(38, 0)", "snowflake"), - ("VARCHAR", "snowflake"), - ("TIMESTAMP_NTZ(9)", "snowflake"), - ), - expected_answered=3, - ), - ], - ids=lambda case: case.description, -) -def test_given_native_engine_when_normalizing_publicly_then_native_answers( - test_case: PublicNativeTypeTestCase, monkeypatch: pytest.MonkeyPatch -) -> None: - calls: list[tuple[str, str]] = [] - answers: list[object] = [] - native_normalize: Callable[[str, str], object] = native_module.normalize_type - - def spy(type_sql: str, dialect: str) -> object: - calls.append((type_sql, dialect)) - answers.append(native_normalize(type_sql, dialect)) - return answers[-1] - - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, test_case.engine) - monkeypatch.setattr(native_module, "normalize_type", spy) - cached_normalize_type.cache_clear() - normalized: list[NormalizedType] = [ - normalize_type(type_sql=type_sql, dialect=test_case.dialect) - for type_sql in test_case.type_strings - ] - cached_normalize_type.cache_clear() - - assert ( - tuple(calls), - sum(answer is not None for answer in answers), - len(normalized), - ) == (test_case.expected_native_calls, test_case.expected_answered, len(test_case.type_strings)) - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py b/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py index d646212488..b6b73c5cba 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/_test_types.py @@ -6,7 +6,7 @@ @dataclass(frozen=True) -class GeneratedContractParityTestCase: +class GeneratedContractTestCase: """Generated projects whose models are perturbed into every contract family per dialect.""" description: str @@ -20,11 +20,19 @@ class GeneratedContractParityTestCase: @dataclass(frozen=True) -class DeferredContractTestCase: - """A contract input native validation hands back to Python, and the deferral it records.""" +class FormerlyDeferredContractTestCase: + """A contract input Python used to answer, and the native `(code, model, column, message)`s.""" description: str declared_type: str dialect: str | None - expected_kind: str - expected_deferred_models: int + expected_diagnostics: tuple[tuple[str, str | None, str | None, str], ...] + + +@dataclass(frozen=True) +class UnknownDialectContractTestCase: + """A dialect Polyglot does not know, and the error Python's type normalization raised.""" + + description: str + dialect: str + expected_error: str diff --git a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py index 418254a266..653e5eb3d4 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py @@ -1,9 +1,8 @@ -"""Generated contract inputs, and the Python and native contract results they produce.""" +"""Generated contract inputs, and the native contract results they produce.""" from __future__ import annotations import itertools -import json import random from collections import Counter from collections.abc import Callable, Iterator, Sequence @@ -14,7 +13,6 @@ import pytest import sqlbuild._native as native_module -from sqlbuild.adapter.type_system._helpers.type_normalization import normalize_type from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile.models import ( CompiledModel, @@ -24,13 +22,8 @@ DynamicColumnFamilyProof, InferredColumn, ) -from sqlbuild.compiler.contracts._helpers.evaluation import ( - python_model_contract_diagnostics, - requires_contract_evaluation, -) from sqlbuild.compiler.contracts.main.validate import evaluate_model_contracts from sqlbuild.compiler.discovery.main.discover import discover_project_inputs -from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.pipeline.main.graph import build_project_graph from sqlbuild.compiler.planner.types import ContractPolicy, IncrementalMode, MaterializationType @@ -168,7 +161,7 @@ type ContractView = tuple[CompilerDiagnostic, ...] type NativeContractRequest = tuple[str, bool, list[Any]] -type NativeContractOutcome = tuple[str | None, list[Any]] +type NativeContractOutcome = list[Any] _EMPTY_SCHEMA_ROW: tuple[list[Any], list[Any], bool, bool] = ([], [], False, False) @@ -285,54 +278,15 @@ def _dynamic_proof(*, rng: random.Random) -> DynamicColumnContractProof: ) -def contract_views( - *, - project: CompiledProject, - dialect: str | None, - monkeypatch: pytest.MonkeyPatch, -) -> tuple[ContractView, ContractView]: - """The Python contract diagnostics native defers to, and native's, for the same project.""" - - return ( - _on_engine( - engine="native", - monkeypatch=monkeypatch, - run=lambda: _python_contract_diagnostics(project=project, dialect=dialect), - ), - _on_engine( - engine="native-preview", - monkeypatch=monkeypatch, - run=lambda: evaluate_model_contracts(project=project, dialect=dialect).diagnostics, - ), - ) - - -def _python_contract_diagnostics( - *, project: CompiledProject, dialect: str | None -) -> tuple[CompilerDiagnostic, ...]: - mode: ColumnContractMode = project.settings.column_contract_mode - evaluated: list[CompiledModel] = list( - filter(lambda model: requires_contract_evaluation(model=model, mode=mode), project.models) - ) - diagnostics: list[CompilerDiagnostic] = [] - for model in evaluated: - diagnostics.extend( - python_model_contract_diagnostics(model=model, mode=mode, dialect=dialect) - ) - return tuple(diagnostics) - +def contract_diagnostics(*, project: CompiledProject, dialect: str | None) -> ContractView: + """The project's contract diagnostics, through the public contract entry point.""" -def _on_engine( - *, engine: str, monkeypatch: pytest.MonkeyPatch, run: Callable[[], ContractView] -) -> ContractView: - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine) - normalize_type.cache_clear() - return run() + return evaluate_model_contracts(project=project, dialect=dialect).diagnostics class NativeContractRecord(NamedTuple): - """Native contract outcomes: `native` (evaluated natively), `typed_comparisons`, handback - kinds and `native_diagnostics` in `statuses`; codes of natively built diagnostics in `codes`.""" + """Native contract outcomes: `native` (evaluated natively), `typed_comparisons` and + `native_diagnostics` in `statuses`; codes of natively built diagnostics in `codes`.""" statuses: Counter[str] codes: Counter[str] @@ -349,10 +303,7 @@ def record_native_outcomes(*, monkeypatch: pytest.MonkeyPatch) -> NativeContract def counted(request: NativeContractRequest) -> list[NativeContractOutcome]: outcomes: list[NativeContractOutcome] = evaluate(request) record.statuses.update(native_contract_statuses(request=request, outcomes=outcomes)) - answered: list[NativeContractOutcome] = list(filter(_answered, outcomes)) - rows: Iterator[Sequence[Any]] = itertools.chain.from_iterable( - diagnostics for _, diagnostics in answered - ) + rows: Iterator[Sequence[Any]] = itertools.chain.from_iterable(outcomes) record.codes.update(row[0] for row in rows) return outcomes @@ -363,37 +314,22 @@ def counted(request: NativeContractRequest) -> list[NativeContractOutcome]: def native_contract_statuses( *, request: NativeContractRequest, outcomes: list[NativeContractOutcome] ) -> Counter[str]: - """Per-request counts: `native` for models native evaluated, `typed_comparisons` for those - that compared at least one typed column, each handback kind, and `native_diagnostics`.""" + """Per-request counts: `native` for evaluated models, `typed_comparisons` for those that + compared at least one typed column, and `native_diagnostics`.""" _, implicit, models = request - pairs: list[tuple[Sequence[Any], NativeContractOutcome]] = list( - zip(models, outcomes, strict=True) - ) - answered: list[tuple[Sequence[Any], NativeContractOutcome]] = list( - filter(lambda pair: _answered(pair[1]), pairs) - ) evaluated: list[Sequence[Any]] = list( - filter( - lambda model: _requires_evaluation(model=model, implicit=implicit), - (model for model, _ in answered), - ) - ) - statuses: Counter[str] = Counter( - str(deferral) for deferral, _ in filter(lambda outcome: not _answered(outcome), outcomes) + filter(lambda model: _requires_evaluation(model=model, implicit=implicit), models) ) + statuses: Counter[str] = Counter() statuses["native"] += len(evaluated) statuses["typed_comparisons"] += sum( _compares_typed_column(model=model, implicit=implicit) for model in evaluated ) - statuses["native_diagnostics"] += sum(len(diagnostics) for _, (_, diagnostics) in answered) + statuses["native_diagnostics"] += sum(len(diagnostics) for diagnostics in outcomes) return statuses -def _answered(outcome: NativeContractOutcome) -> bool: - return outcome[0] is None - - def _schema(model: Sequence[Any]) -> Sequence[Any]: return (_EMPTY_SCHEMA_ROW, model[2])[model[2] is not None] @@ -420,16 +356,6 @@ def _compares_typed_column(*, model: Sequence[Any], implicit: bool) -> bool: ) -def deferral_records(directory: Path) -> list[dict[str, str]]: - """Every deferral record written under `directory`, in file order.""" - - lines: Iterator[str] = itertools.chain.from_iterable( - path.read_text(encoding="utf-8").splitlines() - for path in sorted(directory.glob("analysis-deferrals-*.jsonl")) - ) - return [json.loads(line) for line in lines] - - def with_declared_type(*, project: CompiledProject, declared_type: str) -> CompiledProject: """The project with its enforced staging model declaring `declared_type` for every column.""" diff --git a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py index 65b4aa8400..3aa7929c5d 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py @@ -1,4 +1,4 @@ -"""Native contract validation and promotion conflicts equal Python's on generated projects.""" +"""Native contract validation on generated projects and on inputs Python once answered.""" from __future__ import annotations @@ -8,27 +8,25 @@ import pytest from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.contracts._test_types import ( - DeferredContractTestCase, - GeneratedContractParityTestCase, + FormerlyDeferredContractTestCase, + GeneratedContractTestCase, + UnknownDialectContractTestCase, ) from tests.integration.src.sqlbuild.compiler.contracts.helpers import ( NativeContractRecord, compiled_contract_project, - contract_views, - deferral_records, + contract_diagnostics, perturbed_project, record_native_outcomes, with_declared_type, ) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches @pytest.mark.parametrize( "test_case", [ - GeneratedContractParityTestCase( + GeneratedContractTestCase( description="enforced, implicit, typed, nullability and dynamic contracts", seed=20261009, variants=60, @@ -41,78 +39,82 @@ ], ids=lambda case: case.description, ) -def test_given_generated_contracts_when_validating_natively_then_diagnostics_match_python( - test_case: GeneratedContractParityTestCase, +def test_given_generated_contracts_when_validating_then_every_contract_family_is_answered( + test_case: GeneratedContractTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: rng: random.Random = random.Random(test_case.seed) record: NativeContractRecord = record_native_outcomes(monkeypatch=monkeypatch) base: CompiledProject = compiled_contract_project(project_dir=tmp_path / "project") - labels: list[object] = [] - python_views: list[object] = [] - native_views: list[object] = [] - for variant in range(test_case.variants): + for _ in range(test_case.variants): project: CompiledProject = perturbed_project(project=base, rng=rng) for dialect in test_case.dialects: - python, native = contract_views( - project=project, dialect=dialect, monkeypatch=monkeypatch - ) - labels.append((variant, dialect)) - python_views.append(python) - native_views.append(native) + _ = contract_diagnostics(project=project, dialect=dialect) - assert mismatches(inputs=labels, expected=python_views, actual=native_views) == [] - assert record.statuses["native"] >= test_case.expected_minimum_native - assert record.statuses["typed_comparisons"] >= test_case.expected_minimum_typed_comparisons - assert record.statuses["native_diagnostics"] >= test_case.expected_minimum_diagnostics - assert set(record.codes) >= test_case.expected_codes + assert ( + record.statuses["native"] >= test_case.expected_minimum_native, + record.statuses["typed_comparisons"] >= test_case.expected_minimum_typed_comparisons, + record.statuses["native_diagnostics"] >= test_case.expected_minimum_diagnostics, + set(record.codes) >= test_case.expected_codes, + ) == (True, True, True, True), test_case.description @pytest.mark.parametrize( "test_case", [ - DeferredContractTestCase( - description="a declared type outside ASCII", + FormerlyDeferredContractTestCase( + description="a declared type outside ASCII upper-cases as Python does", declared_type="TÉXT", dialect="duckdb", - expected_kind="type_normalization", - expected_deferred_models=1, + expected_diagnostics=(), ), - DeferredContractTestCase( - description="a dialect outside the native type system", + FormerlyDeferredContractTestCase( + description="a dialect outside the old native build", declared_type="INTEGER", dialect="mysql", - expected_kind="type_normalization", - expected_deferred_models=2, + expected_diagnostics=(), ), ], ids=lambda case: case.description, ) -def test_given_input_native_cannot_answer_when_validating_then_python_answers_and_it_is_recorded( - test_case: DeferredContractTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, +def test_given_input_python_once_answered_when_validating_then_native_diagnostics_are_returned( + test_case: FormerlyDeferredContractTestCase, tmp_path: Path ) -> None: - record_dir: Path = tmp_path / "records" - record: NativeContractRecord = record_native_outcomes(monkeypatch=monkeypatch) project: CompiledProject = with_declared_type( project=compiled_contract_project(project_dir=tmp_path / "project"), declared_type=test_case.declared_type, ) - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) - python, native = contract_views( - project=project, dialect=test_case.dialect, monkeypatch=monkeypatch - ) + diagnostics = contract_diagnostics(project=project, dialect=test_case.dialect) - assert native == python - assert record.statuses[test_case.expected_kind] == test_case.expected_deferred_models assert ( - deferral_records(record_dir) - == [{"kind": test_case.expected_kind, "site": "contracts/columns.py"}] - * test_case.expected_deferred_models - ) + tuple( + (diagnostic.code, diagnostic.resource_name, diagnostic.column_name, diagnostic.message) + for diagnostic in diagnostics + ) + == test_case.expected_diagnostics + ), test_case.description + + +@pytest.mark.parametrize( + "test_case", + [ + UnknownDialectContractTestCase( + description="a dialect Polyglot does not know raises the wheel's error", + dialect="motherduck", + expected_error="Unknown dialect: motherduck", + ), + ], + ids=lambda case: case.description, +) +def test_given_unknown_dialect_when_validating_typed_contracts_then_python_error_is_raised( + test_case: UnknownDialectContractTestCase, tmp_path: Path +) -> None: + project: CompiledProject = compiled_contract_project(project_dir=tmp_path / "project") + + with pytest.raises(ValueError, match=test_case.expected_error): + _ = contract_diagnostics(project=project, dialect=test_case.dialect) if __name__ == "__main__": diff --git a/tests/unit/src/sqlbuild/adapter/type_system/test_type_normalization.py b/tests/unit/src/sqlbuild/adapter/type_system/test_type_normalization.py index 3603293450..f81fcf9e0c 100644 --- a/tests/unit/src/sqlbuild/adapter/type_system/test_type_normalization.py +++ b/tests/unit/src/sqlbuild/adapter/type_system/test_type_normalization.py @@ -1,9 +1,7 @@ from __future__ import annotations import pytest -from polyglot_sql import PolyglotError -import sqlbuild.adapter.type_system._helpers.type_normalization as type_normalization from sqlbuild.adapter.contract.models import NormalizedType from sqlbuild.adapter.contract.types import TypeFamily from sqlbuild.adapter.type_system.main.normalize_numeric_family import normalize_numeric_family @@ -16,15 +14,6 @@ ) -class _UnsupportedTypePolyglot: - PolyglotError: type[PolyglotError] = PolyglotError - - @staticmethod - def parse_data_type(*args: object, **kwargs: object) -> object: - del args, kwargs - raise PolyglotError("unsupported test type") - - @pytest.mark.parametrize( "test_case", [ @@ -62,40 +51,6 @@ def test_given_type_string_when_normalizing_then_it_returns_expected_shape( assert result == test_case.expected_type -@pytest.mark.parametrize( - "test_case", - [ - TypeNormalizationTestCase( - description="fallback normalizes bigquery integer alias", - dialect="bigquery", - raw_type="INTEGER", - expected_type=NormalizedType(normalized_name="INT64", family=TypeFamily.INTEGER), - ), - TypeNormalizationTestCase( - description="fallback preserves decimal precision and scale", - dialect="snowflake", - raw_type="NUMBER(10,2)", - expected_type=NormalizedType( - normalized_name="DECIMAL(10,2)", - family=TypeFamily.DECIMAL, - precision=10, - scale=2, - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_type_string_when_normalizing_without_polyglot_then_it_returns_expected_shape( - test_case: TypeNormalizationTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - monkeypatch.setattr(type_normalization, "import_polyglot", _UnsupportedTypePolyglot) - - result: NormalizedType = normalize_type(type_sql=test_case.raw_type, dialect=test_case.dialect) - - assert result == test_case.expected_type - - @pytest.mark.parametrize( "test_case", [ @@ -170,69 +125,6 @@ def test_given_type_strings_when_comparing_then_it_returns_expected_equality( assert result is test_case.expected_equal -@pytest.mark.parametrize( - "test_case", - [ - TypeEqualityTestCase( - description="fallback treats bigquery string aliases as equal", - dialect="bigquery", - left_type="STRING", - right_type="TEXT", - expected_equal=True, - ), - TypeEqualityTestCase( - description="fallback keeps decimal scale differences distinct", - dialect="snowflake", - left_type="NUMBER(10,2)", - right_type="NUMBER(10,3)", - expected_equal=False, - ), - TypeEqualityTestCase( - description="fallback treats snowflake timestamp alias as timestamp_ntz", - dialect="snowflake", - left_type="TIMESTAMP", - right_type="TIMESTAMP_NTZ", - expected_equal=True, - ), - TypeEqualityTestCase( - description="fallback treats snowflake integer as number 38 scale 0", - dialect="snowflake", - left_type="INTEGER", - right_type="NUMBER(38,0)", - expected_equal=True, - ), - TypeEqualityTestCase( - description="fallback treats snowflake text as default varchar", - dialect="snowflake", - left_type="TEXT", - right_type="VARCHAR(16777216)", - expected_equal=True, - ), - TypeEqualityTestCase( - description="fallback treats bare snowflake number as number 38 scale 0", - dialect="snowflake", - left_type="NUMBER", - right_type="NUMBER(38,0)", - expected_equal=True, - ), - ], - ids=lambda case: case.description, -) -def test_given_type_strings_when_comparing_without_polyglot_then_it_returns_expected_equality( - test_case: TypeEqualityTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - monkeypatch.setattr(type_normalization, "import_polyglot", _UnsupportedTypePolyglot) - - result: bool = types_equal( - left=test_case.left_type, - right=test_case.right_type, - dialect=test_case.dialect, - ) - - assert result is test_case.expected_equal - - @pytest.mark.parametrize( "test_case", [ @@ -266,29 +158,3 @@ def test_given_type_string_when_resolving_numeric_family_then_it_returns_expecte ) assert result == test_case.expected_family - - -@pytest.mark.parametrize( - "test_case", - [ - NumericFamilyTestCase( - description="fallback classifies float64 as float family", - dialect="bigquery", - raw_type="FLOAT64", - expected_family="float", - ) - ], - ids=lambda case: case.description, -) -def test_given_type_string_without_polyglot_when_resolving_numeric_family_then_it_returns_expected( - test_case: NumericFamilyTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - monkeypatch.setattr(type_normalization, "import_polyglot", _UnsupportedTypePolyglot) - - result: str | None = normalize_numeric_family( - type_sql=test_case.raw_type, - dialect=test_case.dialect, - ) - - assert result == test_case.expected_family From cb862b90c5ebe3ec17cb56ad4ebed44751f98479 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:18:09 +0100 Subject: [PATCH 06/36] fix(native): normalize nested types on the caller's stack, reading only top-level type fields --- Cargo.lock | 1 - crates/sqlbuild-analysis/Cargo.toml | 1 - .../src/contracts/_helpers/columns.rs | 4 +- .../src/contracts/_helpers/dynamic.rs | 3 +- .../src/contracts/tests/helpers.rs | 5 +- .../src/type_system/_helpers/mod.rs | 1 + .../src/type_system/_helpers/parsed.rs | 14 +- .../src/type_system/_helpers/shallow_args.rs | 445 ++++++++++++++++++ .../src/type_system/constants.rs | 4 - .../src/type_system/main/normalize_type.rs | 14 - .../type_system/tests/test_normalize_type.rs | 29 +- .../adapter/type_system/_test_types.py | 2 +- .../test_native_type_normalization.py | 13 +- .../contracts/test_native_model_contracts.py | 60 ++- 14 files changed, 540 insertions(+), 56 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs diff --git a/Cargo.lock b/Cargo.lock index c4a8cedcaf..0d533db2ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,7 +707,6 @@ dependencies = [ "sqlbuild-model-config", "sqlbuild-scopes", "sqlbuild-sqltext", - "stacker", ] [[package]] diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 6b963edcb8..4a1d764352 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -25,7 +25,6 @@ sqlbuild-core.workspace = true sqlbuild-model-config.workspace = true sqlbuild-scopes.workspace = true sqlbuild-sqltext.workspace = true -stacker.workspace = true [lints] workspace = true diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs index 469815df59..a436fdf4ed 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs @@ -13,8 +13,8 @@ use crate::contracts::constants::{ UNPROVEN_TYPE_OUTPUT_MESSAGE, }; use crate::contracts::models::{ - ContractDiagnostic, ContractLocation, ContractModel, ContractSchema, - ContractSeverity, DeclaredColumn, InferredOutputColumn, RelatedOutput, + ContractDiagnostic, ContractLocation, ContractModel, ContractSchema, ContractSeverity, + DeclaredColumn, InferredOutputColumn, RelatedOutput, }; use crate::type_system::models::TypeNormalizationError; diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs index d48ac54779..19a2dee914 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs @@ -12,8 +12,7 @@ use crate::contracts::constants::{ TYPE_MISMATCH_CODE, UNKNOWN_TYPE_CODE, }; use crate::contracts::models::{ - ContractDiagnostic, ContractLocation, ContractModel, ContractSeverity, - DynamicContractProof, + ContractDiagnostic, ContractLocation, ContractModel, ContractSeverity, DynamicContractProof, }; use crate::type_system::models::TypeNormalizationError; diff --git a/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs b/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs index c18efef848..e573e13ec7 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/helpers.rs @@ -1,7 +1,6 @@ use crate::contracts::models::{ - ContractDiagnostic, ContractModel, ContractSchema, DeclaredColumn, - DeclaredColumnFamily, DynamicContractProof, InferredOutputColumn, PromotionModel, - PromotionRequest, + ContractDiagnostic, ContractModel, ContractSchema, DeclaredColumn, DeclaredColumnFamily, + DynamicContractProof, InferredOutputColumn, PromotionModel, PromotionRequest, }; pub(crate) fn model(contract: Option<&str>, schema: Option) -> ContractModel { diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/mod.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/mod.rs index 51643d4b4a..70cbe23166 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/mod.rs @@ -2,3 +2,4 @@ pub(crate) mod dialects; pub(crate) mod fallback; pub(crate) mod parsed; pub(crate) mod python_text; +pub(crate) mod shallow_args; diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs index 81c419168d..a3ded58057 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs @@ -8,6 +8,7 @@ use crate::type_system::_helpers::fallback::{ timestamp_normalized_name, }; use crate::type_system::_helpers::python_text::{python_int, split_type_and_params, upper}; +use crate::type_system::_helpers::shallow_args::shallow_args; use crate::type_system::constants::{ BIGNUMERIC_TYPE_NAME, BOOLEAN_TYPE_NAMES, CUSTOM_NORMALIZATION_TYPE_NAMES, DATE_TYPE_NAME, DATETIME_TYPE_NAME, DECIMAL_TYPE_NAMES, FLOAT_TYPE_NAMES, FLOAT_WIRE_TYPE_NAME, @@ -40,12 +41,12 @@ pub(crate) fn normalize_parsed( polyglot: &Dialect, dialect: Option, ) -> Result { + let args: Map = shallow_args(&parsed); let expression: Expression = Expression::DataType(parsed); let generated: String = polyglot .generate(&expression) .map_err(|error| TypeNormalizationError::Generation(error.to_string()))?; let normalized_name: String = upper(&generated).replace(' ', ""); - let args: Map = expression_args(&expression); let dtype_name: String = polyglot_type_name(&args); let params: Vec = polyglot_type_params(&args); let snowflake: bool = dialect == Some(TypeDialect::Snowflake); @@ -153,17 +154,6 @@ fn snowflake_number(raw_name: &str) -> NormalizedType { } } -/// The wheel's `Expression.args`: the serde payload of the expression's variant. -fn expression_args(expression: &Expression) -> Map { - match serde_json::to_value(expression) { - Ok(Value::Object(map)) => match map.into_iter().next() { - Some((_, Value::Object(payload))) => payload, - _ => Map::new(), - }, - _ => Map::new(), - } -} - /// Python's `_polyglot_type_name`. fn polyglot_type_name(args: &Map) -> String { let data_type: String = upper(&match args.get("data_type") { diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs new file mode 100644 index 0000000000..c993b8418f --- /dev/null +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs @@ -0,0 +1,445 @@ +//! The wheel's `DataType.args` scalars, read without walking nested types. +//! +//! Serializing a whole parsed type recurses once per nesting level; normalization only reads +//! the top-level tag, name and size parameters, so nested values are skipped unvisited. + +use std::fmt::Display; + +use serde::ser::{ + Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, + SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer, +}; +use serde_json::{Map, Number, Value}; + +/// The top-level scalar fields of one serialized value; nested values are absent. +pub(crate) fn shallow_args(value: &T) -> Map { + value.serialize(Fields::default()).unwrap_or_default() +} + +#[derive(Debug)] +pub(crate) struct Unsupported; + +impl Display for Unsupported { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("not a struct") + } +} + +impl std::error::Error for Unsupported {} + +impl serde::ser::Error for Unsupported { + fn custom(_message: T) -> Self { + Self + } +} + +/// Collects one struct's scalar fields. +#[derive(Default)] +struct Fields { + fields: Map, +} + +impl SerializeStruct for Fields { + type Ok = Map; + type Error = Unsupported; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Unsupported> { + if let Some(scalar) = value.serialize(Scalar)? { + let _ = self.fields.insert(key.to_owned(), scalar); + } + Ok(()) + } + + fn end(self) -> Result, Unsupported> { + Ok(self.fields) + } +} + +impl SerializeStructVariant for Fields { + type Ok = Map; + type Error = Unsupported; + + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Unsupported> { + SerializeStruct::serialize_field(self, key, value) + } + + fn end(self) -> Result, Unsupported> { + Ok(self.fields) + } +} + +impl SerializeMap for Fields { + type Ok = Map; + type Error = Unsupported; + + fn serialize_key(&mut self, _key: &T) -> Result<(), Unsupported> { + Err(Unsupported) + } + + fn serialize_value(&mut self, _value: &T) -> Result<(), Unsupported> { + Err(Unsupported) + } + + fn end(self) -> Result, Unsupported> { + Ok(self.fields) + } +} + +macro_rules! not_a_struct { + ($($method:ident($($argument:ty),*)),* $(,)?) => { + $(fn $method(self, $(_: $argument),*) -> Result { + Err(Unsupported) + })* + }; +} + +impl Serializer for Fields { + type Ok = Map; + type Error = Unsupported; + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + + not_a_struct!( + serialize_bool(bool), + serialize_i8(i8), + serialize_i16(i16), + serialize_i32(i32), + serialize_i64(i64), + serialize_u8(u8), + serialize_u16(u16), + serialize_u32(u32), + serialize_u64(u64), + serialize_f32(f32), + serialize_f64(f64), + serialize_char(char), + serialize_str(&str), + serialize_bytes(&[u8]), + serialize_none(), + serialize_unit(), + serialize_unit_struct(&'static str), + serialize_unit_variant(&'static str, u32, &'static str), + ); + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _value: &T, + ) -> Result { + Err(Unsupported) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(Unsupported) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(Unsupported) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Unsupported) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Unsupported) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(self) + } + + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(self) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Ok(self) + } +} + +/// One field's value if it is a scalar; nested values are skipped without visiting them. +struct Scalar; + +/// Swallows the children of a nested value unvisited. +struct Skipped; + +macro_rules! skip_children { + ($($trait:ident :: $method:ident),* $(,)?) => { + $(impl $trait for Skipped { + type Ok = Option; + type Error = Unsupported; + + fn $method(&mut self, _value: &T) -> Result<(), Unsupported> { + Ok(()) + } + + fn end(self) -> Result, Unsupported> { + Ok(None) + } + })* + }; +} + +skip_children!( + SerializeSeq::serialize_element, + SerializeTuple::serialize_element, + SerializeTupleStruct::serialize_field, + SerializeTupleVariant::serialize_field, +); + +impl SerializeMap for Skipped { + type Ok = Option; + type Error = Unsupported; + + fn serialize_key(&mut self, _key: &T) -> Result<(), Unsupported> { + Ok(()) + } + + fn serialize_value(&mut self, _value: &T) -> Result<(), Unsupported> { + Ok(()) + } + + fn end(self) -> Result, Unsupported> { + Ok(None) + } +} + +impl SerializeStruct for Skipped { + type Ok = Option; + type Error = Unsupported; + + fn serialize_field( + &mut self, + _key: &'static str, + _value: &T, + ) -> Result<(), Unsupported> { + Ok(()) + } + + fn end(self) -> Result, Unsupported> { + Ok(None) + } +} + +impl SerializeStructVariant for Skipped { + type Ok = Option; + type Error = Unsupported; + + fn serialize_field( + &mut self, + _key: &'static str, + _value: &T, + ) -> Result<(), Unsupported> { + Ok(()) + } + + fn end(self) -> Result, Unsupported> { + Ok(None) + } +} + +fn number(value: impl Into) -> Result, Unsupported> { + Ok(Some(Value::Number(value.into()))) +} + +impl Serializer for Scalar { + type Ok = Option; + type Error = Unsupported; + type SerializeSeq = Skipped; + type SerializeTuple = Skipped; + type SerializeTupleStruct = Skipped; + type SerializeTupleVariant = Skipped; + type SerializeMap = Skipped; + type SerializeStruct = Skipped; + type SerializeStructVariant = Skipped; + + fn serialize_bool(self, value: bool) -> Result { + Ok(Some(Value::Bool(value))) + } + + fn serialize_i8(self, value: i8) -> Result { + number(value) + } + + fn serialize_i16(self, value: i16) -> Result { + number(value) + } + + fn serialize_i32(self, value: i32) -> Result { + number(value) + } + + fn serialize_i64(self, value: i64) -> Result { + number(value) + } + + fn serialize_u8(self, value: u8) -> Result { + number(value) + } + + fn serialize_u16(self, value: u16) -> Result { + number(value) + } + + fn serialize_u32(self, value: u32) -> Result { + number(value) + } + + fn serialize_u64(self, value: u64) -> Result { + number(value) + } + + fn serialize_f32(self, value: f32) -> Result { + self.serialize_f64(f64::from(value)) + } + + fn serialize_f64(self, value: f64) -> Result { + Ok(Number::from_f64(value).map(Value::Number)) + } + + fn serialize_char(self, value: char) -> Result { + Ok(Some(Value::String(value.to_string()))) + } + + fn serialize_str(self, value: &str) -> Result { + Ok(Some(Value::String(value.to_owned()))) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result { + Ok(None) + } + + fn serialize_none(self) -> Result { + Ok(Some(Value::Null)) + } + + fn serialize_some(self, value: &T) -> Result { + value.serialize(self) + } + + fn serialize_unit(self) -> Result { + Ok(Some(Value::Null)) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Ok(Some(Value::Null)) + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _index: u32, + variant: &'static str, + ) -> Result { + Ok(Some(Value::String(variant.to_owned()))) + } + + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _value: &T, + ) -> Result { + Ok(None) + } + + fn serialize_seq(self, _len: Option) -> Result { + Ok(Skipped) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Ok(Skipped) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(Skipped) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Ok(Skipped) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(Skipped) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { + Ok(Skipped) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Ok(Skipped) + } +} diff --git a/crates/sqlbuild-analysis/src/type_system/constants.rs b/crates/sqlbuild-analysis/src/type_system/constants.rs index db367e2254..d82184dbdc 100644 --- a/crates/sqlbuild-analysis/src/type_system/constants.rs +++ b/crates/sqlbuild-analysis/src/type_system/constants.rs @@ -33,9 +33,5 @@ pub(crate) const POLYGLOT_TYPE_NAME_ALIASES: &[(&str, &str)] = &[ ("TINY_INT", "TINYINT"), ("VAR_CHAR", "VARCHAR"), ]; -/// Brackets a type may nest before normalization moves to a stack sized for its depth. -pub(crate) const SHALLOW_TYPE_NESTING: usize = 256; -/// Stack reserved per nesting level of a deeply nested type: parse, write back, read and drop. -pub(crate) const NESTED_TYPE_STACK_BYTES: usize = 64 * 1024; /// The function call depth limit SQLBuild passes to every Polyglot parse. pub(crate) const MAX_FUNCTION_CALL_DEPTH: usize = 512; diff --git a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs index 1f52a6b068..e017ba45a4 100644 --- a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs @@ -5,7 +5,6 @@ use polyglot_sql::{Dialect, DialectType}; use crate::type_system::_helpers::dialects::coerce_type_dialect; use crate::type_system::_helpers::fallback::normalize_with_fallback; use crate::type_system::_helpers::parsed::{normalize_parsed, parse_type}; -use crate::type_system::constants::{NESTED_TYPE_STACK_BYTES, SHALLOW_TYPE_NESTING}; use crate::type_system::models::{TypeDialect, TypeNormalization, TypeNormalizationError}; /// Normalize one type as Python does for the dialect name it hands Polyglot. @@ -17,19 +16,6 @@ pub fn normalize_type( type_sql: &str, dialect: &str, ) -> Result { - let nesting: usize = type_sql - .bytes() - .filter(|byte| matches!(byte, b'[' | b'<' | b'(')) - .count(); - if nesting <= SHALLOW_TYPE_NESTING { - return normalized(type_sql, dialect); - } - stacker::grow(nesting.saturating_mul(NESTED_TYPE_STACK_BYTES), || { - normalized(type_sql, dialect) - }) -} - -fn normalized(type_sql: &str, dialect: &str) -> Result { let name: &str = if dialect.is_empty() { "generic" } else { diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs index 1c415b32d1..45ea92334b 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs @@ -91,14 +91,17 @@ fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { type_sql: "DECIMAL(99999999999999999999, 2)", dialect: "duckdb", expected_normalization: Ok( - "DECIMAL(99999999999999999999,2) | decimal | 99999999999999999999 | 2 | - | -", + "DECIMAL(99999999999999999999,2) | decimal | 99999999999999999999 | 2 | - | \ + Parse error at line 1, column 30: Invalid number: 99999999999999999999", ), }, NormalizeTypeTestCase { description: "Unicode decimal digits are Python integers", type_sql: "VARCHAR(\u{663})", dialect: "snowflake", - expected_normalization: Ok("VARCHAR(3) | string | - | - | 3 | -"), + expected_normalization: Ok( + "VARCHAR(3) | string | - | - | 3 | Parse error at line 1, column 10: Expected number", + ), }, ]; @@ -119,25 +122,35 @@ fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { } #[test] -fn given_array_suffixes_when_normalizing_on_a_small_stack_then_deep_types_are_normalized() { +fn given_array_suffixes_when_normalizing_on_a_worker_stack_then_python_supported_depths_answer() { let test_cases = [ BracketDepthTestCase { - description: "shallow types stay on the caller's stack", + description: "at the old native bracket cap", depth: 32, expected_native: true, }, BracketDepthTestCase { - description: "deep types move to a stack sized for their depth", - depth: 100_000, + description: "one past the old native bracket cap", + depth: 33, + expected_native: true, + }, + BracketDepthTestCase { + description: "a depth the Python wheel also normalizes, within a debug-build worker stack", + depth: 200, expected_native: true, }, ]; for test_case in test_cases { let type_sql: String = format!("INT{}", "[]".repeat(test_case.depth)); + let expected_name: String = type_sql.clone(); let answered: bool = std::thread::Builder::new() - .stack_size(1024 * 1024) - .spawn(move || normalize_type(&type_sql, "generic").is_ok()) + .stack_size(2 * 1024 * 1024) + .spawn(move || { + normalize_type(&type_sql, "duckdb").is_ok_and(|normalization| { + normalization.normalized.normalized_name == expected_name + }) + }) .expect("the test thread starts") .join() .expect("normalization does not overflow the stack"); diff --git a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py index a35add8b98..723dfd1197 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py @@ -41,7 +41,7 @@ class UnknownDialectTypeTestCase: @dataclass(frozen=True) class DeepTypeTestCase: - """A type deeper than the Polyglot wheel itself could parse.""" + """A deeply nested type the Python wheel normalized.""" description: str type_sql: str diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py index fbc1006264..3e4f247b32 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py @@ -234,20 +234,21 @@ def test_given_unknown_dialect_when_normalizing_then_python_error_is_raised( "test_case", [ DeepTypeTestCase( - description="100,000 array suffixes, past where the wheel itself overflows", - type_sql="INT" + "[]" * 100_000, + description="5,000 array suffixes, a depth the Python wheel normalized", + type_sql="INT" + "[]" * 5_000, expected_family=TypeFamily.OTHER, ), ], ids=lambda case: case.description, ) -def test_given_type_past_the_wheel_depth_when_normalizing_then_it_is_normalized( +def test_given_deeply_nested_type_when_normalizing_then_python_shape_is_returned( test_case: DeepTypeTestCase, ) -> None: - outcome: TypeOutcome = type_outcome(type_sql=test_case.type_sql, dialect="generic") + outcome: TypeOutcome = type_outcome(type_sql=test_case.type_sql, dialect="duckdb") - assert isinstance(outcome.normalized, NormalizedType) - assert outcome.normalized.family == test_case.expected_family + assert outcome.normalized == NormalizedType( + normalized_name=test_case.type_sql, family=test_case.expected_family + ), test_case.description @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py index 3aa7929c5d..21edb544c2 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py @@ -67,13 +67,69 @@ def test_given_generated_contracts_when_validating_then_every_contract_family_is description="a declared type outside ASCII upper-cases as Python does", declared_type="TÉXT", dialect="duckdb", - expected_diagnostics=(), + expected_diagnostics=( + ( + "K002", + "customer_totals", + "total_amount", + "column 'total_amount' inferred as DOUBLE but declared type is DECIMAL(18,2)", + ), + ( + "K005", + "stg_orders", + "customer_id", + "column 'customer_id' is not declared in enforced contract for model 'stg_orders'", + ), + ( + "K005", + "stg_orders", + "status", + "column 'status' is not declared in enforced contract for model 'stg_orders'", + ), + ( + "K002", + "stg_orders", + "order_id", + "column 'order_id' inferred as INTEGER but declared type is TÉXT", + ), + ( + "K002", + "stg_orders", + "amount", + "column 'amount' inferred as DOUBLE but declared type is TÉXT", + ), + ), ), FormerlyDeferredContractTestCase( description="a dialect outside the old native build", declared_type="INTEGER", dialect="mysql", - expected_diagnostics=(), + expected_diagnostics=( + ( + "K002", + "customer_totals", + "total_amount", + "column 'total_amount' inferred as DOUBLE but declared type is DECIMAL(18,2)", + ), + ( + "K005", + "stg_orders", + "customer_id", + "column 'customer_id' is not declared in enforced contract for model 'stg_orders'", + ), + ( + "K005", + "stg_orders", + "status", + "column 'status' is not declared in enforced contract for model 'stg_orders'", + ), + ( + "K002", + "stg_orders", + "amount", + "column 'amount' inferred as DOUBLE but declared type is INTEGER", + ), + ), ), ], ids=lambda case: case.description, From f553ab78e2439bf18514ed849347618180866729 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:29:31 +0100 Subject: [PATCH 07/36] feat(native): analyse non-ASCII models natively and call project-local adapter nullability rules back Python's casefold/upper in CTE facts, legacy analysis and dynamic pivot proofs; pivot parse errors reported as Python reports them; shipped safe_cast rule as data; an adapter's own rules run through an explicit callback whose exceptions reach the caller, bypassing the analysis cache as Python's did. --- .../analysis_session/_helpers/cte_facts.rs | 127 +++++++--------- .../analysis_session/_helpers/dict_walk.rs | 31 +++- .../_helpers/dynamic_pivot.rs | 56 ++----- .../analysis_session/_helpers/enrichment.rs | 7 +- .../analysis_session/_helpers/session.rs | 2 + .../assembly/analysis_session/constants.rs | 4 + .../main/recover_cte_facts.rs | 1 + .../src/assembly/analysis_session/models.rs | 6 +- .../analysis_session/tests/helpers.rs | 3 + .../tests/test_analysis_cache.rs | 4 +- .../tests/test_analysis_session.rs | 41 +++-- .../src/assembly/analysis_session/types.rs | 18 +++ .../project/tests/test_project_assembly.rs | 4 +- .../bindings/_helpers/analysis_session/mod.rs | 1 + .../analysis_session/nullability_rules.rs | 57 +++++++ .../_helpers/analysis_session/session.rs | 35 ++++- .../_helpers/contracts/model_contracts.rs | 6 +- src/sqlbuild/_native.pyi | 5 +- .../_helpers/model_analysis.py | 20 ++- .../analysis_session/_helpers/session_rows.py | 31 ++-- .../compiler/analysis_session/constants.py | 3 + .../compiler/analysis_session/_test_types.py | 21 +-- .../compiler/analysis_session/helpers.py | 41 +---- .../test_native_model_analysis.py | 143 ++++++++++++------ 24 files changed, 405 insertions(+), 262 deletions(-) create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs index e945f3e3b4..d7b018812a 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs @@ -1,12 +1,14 @@ //! Python's CTE fact recovery and filtered non-null outputs, over the wheel's expression view. -use std::cell::Cell; use std::collections::HashSet; use std::rc::Rc; use polyglot_sql::Expression; use polyglot_sql::traversal::ExpressionWalk; use serde_json::{Map, Value}; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::main::python_upper::python_upper; use crate::assembly::analysis_session::_helpers::dict_walk::{ PyValue, alias_or_name, arg, children, column_table_name, direct_tables, expression_args, @@ -21,17 +23,17 @@ use crate::assembly::analysis_session::constants::{ CONCAT_AST_KIND, CONDITIONAL_RESULT_RULE, COUNT_AST_KIND, CUSTOM_TYPE_NAME, DECIMAL_TYPE, FIRST_ARG_RULE, FUNCTION_AST_KIND, IF_FUNC_AST_KIND, IS_NULL_AST_KIND, JOIN_FULL, JOIN_LEFT, JOIN_RIGHT, LITERAL_AST_KIND, NON_NULL_NULLABILITY, NULL_AST_KIND, NULL_SET_OPERATION_TYPE, - NULLABLE_NULLABILITY, NULLIF_FUNCTION_NAME, POLYGLOT_TYPE_NAMES, SELECT_AST_KIND, - SET_OPERATION_AST_KINDS, STRING_LITERAL_TYPE, SUBSTRING_AST_KIND, TABLE_AST_KIND, TEXT_TYPE, - TIMESTAMP_TYPE_NAME, TIMESTAMP_TZ_TYPE, TRY_CAST_AST_KIND, TYPE_PASSTHROUGH_AST_KINDS, - UNKNOWN_NULLABILITY, VARCHAR_DATA_TYPES, WILDCARD, + NULLABLE_NULLABILITY, NULLIF_FUNCTION_NAME, POLYGLOT_TYPE_NAMES, PYTHON_RULE, SAFE_CAST_RULE, + SELECT_AST_KIND, SET_OPERATION_AST_KINDS, STRING_LITERAL_TYPE, SUBSTRING_AST_KIND, + TABLE_AST_KIND, TEXT_TYPE, TIMESTAMP_TYPE_NAME, TIMESTAMP_TZ_TYPE, TRY_CAST_AST_KIND, + TYPE_PASSTHROUGH_AST_KINDS, UNKNOWN_NULLABILITY, VARCHAR_DATA_TYPES, WILDCARD, }; use crate::assembly::analysis_session::constants::{ CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; use crate::type_system::main::normalize_type::normalize_type; use crate::type_system::models::{NormalizedType, TypeFamily}; @@ -51,8 +53,10 @@ type Fact = Result; pub(crate) struct RecoveryProfile<'a> { pub(crate) dialect: &'a str, pub(crate) function_return_types: &'a Pairs, - /// Adapter nullability rules by function name; None where a rule is not one Python ships. + /// Adapter nullability rules by function name. pub(crate) rules: Option<&'a Pairs>, + /// Runs the adapter's own rules, those with the `python` rule id. + pub(crate) callback: Option<&'a NullabilityCallback>, } /// One enrichment's recovery request. @@ -77,20 +81,6 @@ pub(crate) struct Recovery { /// Python's CTE pass-through facts and filtered non-null outputs, or why they defer. pub(crate) fn recovery(input: &RecoveryInput<'_>) -> Fact { - NON_ASCII_FOLDED.set(false); - let recovered: Fact = recovered(input); - if NON_ASCII_FOLDED.replace(false) { - return Err("non-ASCII text Python casefolds or upper-cases differently".to_owned()); - } - recovered -} - -thread_local! { - /// Whether this recovery folded non-ASCII text, where Python's `casefold`/`upper` may differ. - static NON_ASCII_FOLDED: Cell = const { Cell::new(false) }; -} - -fn recovered(input: &RecoveryInput<'_>) -> Fact { let mut recovery: Recovery = Recovery::default(); let mut parsed: Option> = None; if input.recover { @@ -138,15 +128,6 @@ pub(crate) struct LegacyAnalysis { /// Python's legacy analysis of a model the native engine handed back, or why it defers. pub(crate) fn legacy_analysis(input: &LegacyInput<'_>) -> Fact { - NON_ASCII_FOLDED.set(false); - let analysed: Fact = legacy_analysed(input); - if NON_ASCII_FOLDED.replace(false) { - return Err("non-ASCII text Python casefolds or upper-cases differently".to_owned()); - } - analysed -} - -fn legacy_analysed(input: &LegacyInput<'_>) -> Fact { let Some(parsed) = parse_one(input.cleaned_sql, input.profile.dialect)? else { return Ok(LegacyAnalysis { succeeded: false, @@ -450,9 +431,6 @@ fn passthrough_facts( if ctes.is_empty() { return Ok(recovery); } - if input.profile.rules.is_none() { - return Err("an adapter nullability rule Python does not ship".to_owned()); - } let context = Context { profile: &input.profile, }; @@ -850,14 +828,6 @@ impl Context<'_, '_> { /// Python's `ExpressionInferenceProfile.function_return_type`. fn function_return_type(&self, function_name: &str) -> Fact> { - let ascii_keys: bool = self - .profile - .function_return_types - .iter() - .all(|(declared, _)| declared.is_ascii()); - if ascii_keys && keeps_non_ascii_upper(function_name) { - return Ok(None); - } let upper: String = upper(function_name); Ok(self .profile @@ -964,7 +934,7 @@ impl Context<'_, '_> { None => Ok(UNKNOWN_NULLABILITY), }; } - let rule: Option<&str> = self.rule(node_kind)?; + let rule: Option<(&str, &str)> = self.rule(node_kind)?; if node_kind != COALESCE_AST_KIND && rule.is_none() { return Ok(UNKNOWN_NULLABILITY); } @@ -972,7 +942,7 @@ impl Context<'_, '_> { for child in expression_args(expression) { arguments.push(self.nullability(Some(child), aliases, scoped)?); } - Ok(combined(node_kind, rule, &arguments)) + self.combined(node_kind, rule, &arguments) } /// Python's `_infer_polyglot_shallow_nullability`. @@ -992,7 +962,7 @@ impl Context<'_, '_> { None => Ok(UNKNOWN_NULLABILITY), }; } - let rule: Option<&str> = self.rule(node_kind)?; + let rule: Option<(&str, &str)> = self.rule(node_kind)?; if node_kind != COALESCE_AST_KIND && rule.is_none() { return Ok(UNKNOWN_NULLABILITY); } @@ -1000,20 +970,38 @@ impl Context<'_, '_> { for child in expression_args(expression) { arguments.push(self.shallow_nullability(Some(child))?); } - Ok(combined(node_kind, rule, &arguments)) + self.combined(node_kind, rule, &arguments) } /// `inference_profile.function_nullability_rule(kind)`. - fn rule(&self, node_kind: &str) -> Fact> { - let rules: &Pairs = self + fn rule(&self, node_kind: &str) -> Fact> { + let upper: String = upper(node_kind); + Ok(self .profile .rules - .ok_or("an adapter nullability rule Python does not ship")?; - let upper: String = upper(node_kind); - Ok(rules - .iter() + .into_iter() + .flatten() .find(|(declared, _)| *declared == upper) - .map(|(_, rule)| rule.as_str())) + .map(|(declared, rule)| (declared.as_str(), rule.as_str()))) + } + + /// COALESCE's or an adapter rule's nullability over the argument nullabilities. + fn combined( + &self, + node_kind: &str, + rule: Option<(&str, &str)>, + arguments: &[&'static str], + ) -> Fact<&'static str> { + match rule { + Some((declared, PYTHON_RULE)) if node_kind != COALESCE_AST_KIND => { + let callback: &NullabilityCallback = self + .profile + .callback + .ok_or("an adapter nullability rule without its callback")?; + (callback.0)(declared, arguments) + } + _ => Ok(combined(node_kind, rule.map(|(_, rule)| rule), arguments)), + } } } @@ -1034,10 +1022,20 @@ fn combined(node_kind: &str, rule: Option<&str>, arguments: &[&'static str]) -> match rule { Some(FIRST_ARG_RULE) => arguments.first().copied().unwrap_or(UNKNOWN_NULLABILITY), Some(CONDITIONAL_RESULT_RULE) => conditional_result(arguments), + Some(SAFE_CAST_RULE) => safe_cast(arguments), _ => UNKNOWN_NULLABILITY, } } +/// Python's `safe_cast_nullability`. +fn safe_cast(arguments: &[&'static str]) -> &'static str { + if arguments.first() == Some(&NULLABLE_NULLABILITY) { + NULLABLE_NULLABILITY + } else { + UNKNOWN_NULLABILITY + } +} + /// Python's `conditional_result_nullability`. fn conditional_result(arguments: &[&'static str]) -> &'static str { let [_, when_true, when_false, ..] = arguments else { @@ -1534,31 +1532,14 @@ fn cast_target_type(target: &Map) -> Option { Some(type_name) } -/// Python's `str.casefold`, exact for ASCII; non-ASCII text marks the recovery deferred. +/// Python's `str.casefold`. fn casefold(text: &str) -> String { - flag_non_ascii(text); - text.to_ascii_lowercase() + python_casefold(active_python_text(), text) } -/// Python's `str.upper`, exact for ASCII; non-ASCII text marks the recovery deferred. +/// Python's `str.upper`. fn upper(text: &str) -> String { - flag_non_ascii(text); - text.to_ascii_uppercase() -} - -/// Whether `text.upper()` keeps a non-ASCII character, so it can equal no ASCII key. -fn keeps_non_ascii_upper(text: &str) -> bool { - text.chars().any(upper_is_non_ascii) -} - -fn upper_is_non_ascii(character: char) -> bool { - !character.to_uppercase().all(|upper| upper.is_ascii()) -} - -fn flag_non_ascii(text: &str) { - if !text.is_ascii() { - NON_ASCII_FOLDED.set(true); - } + python_upper(active_python_text(), text) } fn dict_get<'a, V>(dict: &'a [(String, V)], key: &str) -> Option<&'a V> { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs index 96b1a60d02..29939adaf5 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs @@ -4,6 +4,9 @@ use polyglot_sql::expressions::Cte; use polyglot_sql::traversal::ExpressionWalk; use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions, ast_json}; use serde_json::{Map, Value, json}; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::main::python_upper::python_upper; use crate::assembly::analysis_session::constants::{ ALIAS_AST_KIND, ANNOTATED_AST_KIND, COLUMN_AST_KIND, DEPENDENCY_FUNCTION_NAMES, @@ -130,7 +133,7 @@ pub(crate) fn render_type(node: Option<&Value>) -> Option { .iter() .find(|(name, _)| *name == folded) .map_or_else( - || raw_type.replace('_', " ").to_ascii_uppercase(), + || upper(&raw_type.replace('_', " ")), |(_, rendered)| (*rendered).to_owned(), ); if let Some(length) = python_int(node.get("length")) { @@ -154,9 +157,14 @@ pub(crate) fn python_int(value: Option<&Value>) -> Option { } } -/// Python's `str.casefold` for the ASCII text a pivot proof compares. +/// Python's `str.casefold`. pub(crate) fn casefold(text: &str) -> String { - text.to_ascii_lowercase() + python_casefold(active_python_text(), text) +} + +/// Python's `str.upper`. +pub(crate) fn upper(text: &str) -> String { + python_upper(active_python_text(), text) } /// A value the wheel hands Python for one payload key: an expression, a list of them, or data. @@ -169,6 +177,14 @@ pub(crate) enum PyValue { /// The wheel's guarded `parse_one`; Ok(None) is the `PolyglotError` Python catches. pub(crate) fn parse_one(sql: &str, dialect: &str) -> Result, String> { + Ok(parse_one_or_error(sql, dialect)?.ok()) +} + +/// The wheel's guarded `parse_one`, with the message of the `PolyglotError` it raises. +pub(crate) fn parse_one_or_error( + sql: &str, + dialect: &str, +) -> Result, String> { let guard: ComplexityGuardOptions = serde_json::from_value(json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH})) .map_err(|error| error.to_string())?; @@ -180,12 +196,15 @@ pub(crate) fn parse_one(sql: &str, dialect: &str) -> Result, .parse_with_options(sql, &options); let mut expressions: Vec = match parsed { Ok(expressions) => expressions, - Err(_) => return Ok(None), + Err(error) => return Ok(Err(error.to_string())), }; if expressions.len() != 1 { - return Ok(None); + return Ok(Err(format!( + "Expected 1 statement, found {}", + expressions.len() + ))); } - Ok(expressions.pop()) + Ok(expressions.pop().ok_or_else(|| "no statement".to_owned())) } /// `str(getattr(node, "kind", ""))`. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index fe152f03ee..cd9b7a5f82 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -10,7 +10,7 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::analysis_session::_helpers::dict_walk::{ casefold, column_name, dict_list, identifier_name, is_single_wildcard, nested, node_key, - parse_one, payload, relation_name, render_type, to_dict, truthy, + parse_one_or_error, payload, relation_name, render_type, to_dict, truthy, upper, }; use crate::assembly::analysis_session::constants::{ ALIAS_AST_KIND, CAST_AST_KIND, CTE_AST_KIND, DUCKDB_DIALECT, DYNAMIC_VALUE_SOURCE_KINDS, @@ -21,7 +21,7 @@ use crate::assembly::analysis_session::constants::{ use crate::assembly::analysis_session::models::{ ColumnFact, ContractProof, DynamicFamily, PivotModel, PivotOutcome, PivotTables, }; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::assembly::analysis_session::types::Shapes; /// Stands in for Python's `{}`: neither has a node key. static MISSING_EXPRESSION: Value = Value::Null; @@ -101,9 +101,6 @@ pub(crate) fn pivot_outcome( facts.dialect )); } - if !ascii_inputs(sql, families, facts) { - return PivotOutcome::Deferred; - } let parse_dialect: &str = if folded_dialect == MOTHERDUCK_DIALECT { DUCKDB_DIALECT } else { @@ -111,8 +108,8 @@ pub(crate) fn pivot_outcome( }; let outcome: Result = catch_compiler_panic(|| { Ok(match parsed(sql, parse_dialect)? { - Some(root) => proof(&root, &folded_dialect, families, facts), - None => PivotOutcome::Deferred, + Ok(root) => proof(&root, &folded_dialect, families, facts), + Err(error) => failure(format!("dynamic pivot SQL could not be parsed: {error}")), }) }); match outcome { @@ -121,32 +118,11 @@ pub(crate) fn pivot_outcome( } } -/// Python's `parse_one(...).to_dict()`, or None where Python reports a parse error. -fn parsed(sql: &str, dialect: &str) -> Result, String> { - parse_one(sql, dialect)? +/// Python's `parse_one(...).to_dict()`, or the parse error Python reports. +fn parsed(sql: &str, dialect: &str) -> Result, String> { + parse_one_or_error(sql, dialect)? .map(|expression| to_dict(&expression)) - .transpose() -} - -/// Casefolding equals Python's only for ASCII text. -fn ascii_inputs(sql: &str, families: &[DynamicFamily], facts: &PivotFacts<'_>) -> bool { - let shapes = [ - facts.column_types, - facts.authoritative_types, - facts.column_nullability, - ]; - sql.is_ascii() - && families.iter().all(ascii_family) - && facts.families_by_table.iter().all(ascii_families) - && shapes.into_iter().flatten().all(ascii_shape) -} - -fn ascii_families((name, families): &(String, Vec)) -> bool { - name.is_ascii() && families.iter().all(ascii_family) -} - -fn ascii_shape((name, shape): &(String, Pairs)) -> bool { - name.is_ascii() && shape.iter().all(|(column, _)| column.is_ascii()) + .map_or_else(|error| Ok(Err(error)), |value| value.map(Ok)) } fn proof( @@ -378,7 +354,7 @@ fn aggregate_fact<'a>( source_columns: &[ColumnFact], ) -> (String, Option<&'a str>, Option) { let key: &str = node_key(Some(aggregate)); - let mut name: String = key.to_ascii_uppercase(); + let mut name: String = upper(key); let Some(payload) = aggregate.get(key).and_then(Value::as_object) else { return (name, None, None); }; @@ -387,7 +363,7 @@ fn aggregate_fact<'a>( .and_then(Value::as_str) .filter(|authored| !authored.is_empty()) { - name = authored.to_ascii_uppercase(); + name = upper(authored); } let preserving: bool = TYPE_PRESERVING_AGGREGATES.contains(&name.as_str()); let value: Option<&Value> = payload.get("this"); @@ -677,18 +653,6 @@ fn failure(reason: String) -> PivotOutcome { }) } -fn ascii_family(family: &DynamicFamily) -> bool { - [ - &family.name, - &family.pivot_column, - &family.value_column, - &family.aggregate, - &family.data_type, - ] - .iter() - .all(|text| text.is_ascii()) -} - /// Python's `_families_equal` key for one family. fn normalized(family: &DynamicFamily) -> (String, String, String, String, String, Option<&str>) { ( diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index 38ff6dfa2a..0015320b61 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -20,7 +20,7 @@ use crate::assembly::analysis_session::constants::{ WILDCARD, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow, ModelRequest}; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; use crate::semantic_validation::main::normalize::normalize_analysis_sql; use crate::semantic_validation::models::NormalizationInput; @@ -30,8 +30,9 @@ pub(crate) struct EnrichmentInput<'a> { pub(crate) input_schemas: &'a Shapes, pub(crate) dialect: &'a str, pub(crate) function_return_types: &'a Pairs, - /// Adapter nullability rules by function name; None where one is not a rule Python ships. + /// Adapter nullability rules by function name. pub(crate) nullability_rules: Option<&'a Pairs>, + pub(crate) nullability_callback: Option<&'a NullabilityCallback>, } /// Python's re-analysis: its outcome, columns, plain lineage facts and the star flag. @@ -116,6 +117,7 @@ fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Option { dialect: input.dialect, function_return_types: input.function_return_types, rules: input.nullability_rules, + callback: input.nullability_callback, }, }) { Ok(analysis) => analysis, @@ -198,6 +200,7 @@ fn native_projection( dialect: input.dialect, function_return_types: input.function_return_types, rules: input.nullability_rules, + callback: input.nullability_callback, }, }) { Ok(recovery) => recovery, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index f71f52182b..f274248dcf 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -337,6 +337,7 @@ impl AnalysisSession { dialect: &self.request.dialect, function_return_types: &self.request.function_return_types, rules: self.request.nullability_rules.as_ref(), + callback: self.request.nullability_callback.as_ref(), }, }) }) @@ -438,6 +439,7 @@ impl AnalysisSession { dialect: &self.request.dialect, function_return_types: &self.request.function_return_types, nullability_rules: self.request.nullability_rules.as_ref(), + nullability_callback: self.request.nullability_callback.as_ref(), }) .collect(); let pool = self.catalog.native.analysis_pool()?; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index 4bc16c6823..601ed4b493 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -193,6 +193,10 @@ pub(crate) const JOIN_FULL: &str = "FULL"; /// Ids of the adapter nullability rules Python registers, by the function it calls. pub(crate) const FIRST_ARG_RULE: &str = "first_arg"; pub(crate) const CONDITIONAL_RESULT_RULE: &str = "conditional_result"; +/// The rule id of `safe_cast_nullability`. +pub(crate) const SAFE_CAST_RULE: &str = "safe_cast"; +/// The rule id of a project-local adapter's own rule, which the callback runs. +pub(crate) const PYTHON_RULE: &str = "python"; pub(crate) const NON_NULL_NULLABILITY: &str = "non_null"; pub(crate) const NULLABLE_NULLABILITY: &str = "nullable"; /// Bumped whenever the stored outcome or a key's fields change; older entries never match. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs index 84aace7ca4..92b9f05478 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs @@ -28,6 +28,7 @@ fn recovered_facts(request: &CteFactRequest) -> Result { dialect: &request.dialect, function_return_types: &request.function_return_types, rules: request.nullability_rules.as_ref(), + callback: None, }, })?; let mut direct_outputs: Vec = recovered.direct_outputs.into_iter().collect(); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index f8cdd3b737..26e3dd2773 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -9,7 +9,7 @@ use sqlbuild_cache::store::models::NativeStore; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::types::{OutputSources, Pairs, Shapes}; +use crate::assembly::analysis_session::types::{NullabilityCallback, OutputSources, Pairs, Shapes}; use crate::semantic_validation::types::DiagnosticRow; /// One `ref`, `source`, `seed`, `table_fn` or `udf` call a model's SQL makes. @@ -121,8 +121,10 @@ pub struct SessionRequest { /// Whether published shapes keep authored quoting, Python's `inferred_binding_shape` test. pub case_sensitive_shapes: bool, pub function_return_types: Pairs, - /// Adapter nullability rules as `(function name, rule id)`; None where one is not Python's. + /// Adapter nullability rules as `(function name, rule id)`. pub nullability_rules: Option, + /// Runs the adapter's own rules, those with the `python` rule id. + pub nullability_callback: Option, pub rich_type_inference: bool, pub column_types: Shapes, pub column_nullability: Shapes, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 137ae96f67..2923472b07 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -120,6 +120,7 @@ pub(crate) fn orders_request(models: Vec) -> SessionRequest { case_sensitive_shapes: false, function_return_types: Vec::new(), nullability_rules: Some(Vec::new()), + nullability_callback: None, rich_type_inference: true, column_types: raw_orders.clone(), column_nullability: shapes(&[( @@ -439,6 +440,7 @@ pub(crate) fn recovered_facts(sql: &str) -> Option<(Pairs, Pairs, Vec, V dialect: "duckdb", function_return_types: &Vec::new(), rules: Some(&rules), + callback: None, }, }) .ok()?; @@ -516,6 +518,7 @@ pub(crate) fn legacy_lines(sql: &str) -> Option> { dialect: "duckdb", function_return_types: &Vec::new(), rules: Some(&rules), + callback: None, }, }) .ok()?; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs index b531a45915..7ff569e36b 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs @@ -87,7 +87,7 @@ fn given_a_filled_cache_when_rerunning_then_hits_match_the_uncached_session() { expected_stats: [(0, 2, 2), (2, 0, 0)], }, CacheReuseTestCase { - description: "a model Python analyses is never stored", + description: "non-ASCII models analysed natively are stored", models: &[ ( "events", @@ -103,7 +103,7 @@ fn given_a_filled_cache_when_rerunning_then_hits_match_the_uncached_session() { &["events"], ), ], - expected_stats: [(0, 2, 1), (1, 1, 0)], + expected_stats: [(0, 2, 2), (2, 0, 0)], }, CacheReuseTestCase { description: "a natively answered legacy fallback and a failed analysis are stored", diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 9e9b9bd6db..6bd8c48051 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -106,7 +106,7 @@ fn given_models_when_running_the_session_then_defers_and_publishes_as_python_doe ], }, SessionTestCase { - description: "a non-ASCII filter column Python casefolds still defers to Python", + description: "a non-ASCII filter column is analysed natively", models: &[ ( "events", @@ -123,15 +123,20 @@ fn given_models_when_running_the_session_then_defers_and_publishes_as_python_doe ), ], expected_steps: &[ - &["publish events gr\u{f6}\u{df}e:UNKNOWN", "defer analysis 1"], - &[], + &[ + "publish events gr\u{f6}\u{df}e:UNKNOWN", + "publish events_mart gr\u{f6}\u{df}e:UNKNOWN", + ], ], expected_outcomes: &[ &[ "gr\u{f6}\u{df}e - unknown", "succeeded=true star=false/false diagnostics=[]", ], - &["succeeded=true star=false/false diagnostics=[]"], + &[ + "gr\u{f6}\u{df}e - non_null", + "succeeded=true star=false/false diagnostics=[]", + ], ], }, SessionTestCase { @@ -209,7 +214,7 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe ], }, SessionFactsTestCase { - description: "a model Python analysed keeps no session facts", + description: "a non-ASCII model analysed natively keeps its session facts", models: &[ ( "events", @@ -227,7 +232,7 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe ], expected_facts: &[ "Some([\"gr\u{f6}\u{df}e\"]) gr\u{f6}\u{df}e<-[(\"raw_events\", \"event_id\")]", - "none", + "Some([\"gr\u{f6}\u{df}e\"]) gr\u{f6}\u{df}e<-[(\"events\", \"gr\u{f6}\u{df}e\")]", ], }, ]; @@ -378,14 +383,20 @@ fn given_dynamic_pivots_when_proving_then_matches_python_or_defers() { dialect: "duckdb", sql: "PIVOT FROM WHERE", family: Some(("status", "amount", "MAX")), - expected_outcome: PivotOutcome::Deferred, + expected_outcome: failed( + "dynamic pivot SQL could not be parsed: Parse error at line 1, column 11: \ + Unexpected token: From", + ), }, PivotTestCase { - description: "non-ASCII names Python casefolds differently", + description: "non-ASCII names fold as Python casefolds them", dialect: "duckdb", sql: "SELECT * FROM __ref(\"stra\u{df}e\")", family: Some(("status", "amount", "MAX")), - expected_outcome: PivotOutcome::Deferred, + expected_outcome: failed( + "dynamic-family passthrough from 'stra\u{df}e' must redeclare the upstream \ + families exactly", + ), }, PivotTestCase { description: "no declared families", @@ -466,9 +477,9 @@ fn given_cte_reads_when_recovering_facts_then_matches_python_or_defers() { expected_facts: Some((&[], &[], &[], &["order_id"])), }, CteRecoveryTestCase { - description: "non-ASCII names Python casefolds differently", + description: "non-ASCII names fold as Python casefolds them", sql: "WITH b\u{e4}se AS (SELECT order_id FROM orders) SELECT order_id FROM b\u{e4}se", - expected_facts: None, + expected_facts: Some((&[("order_id", "INTEGER")], &[], &["order_id"], &[])), }, ]; for test_case in test_cases { @@ -559,9 +570,13 @@ fn given_handed_back_models_when_analysing_legacy_then_matches_python_or_defers( ]), }, LegacyAnalysisTestCase { - description: "non-ASCII names Python casefolds differently", + description: "non-ASCII names fold as Python casefolds them", sql: "SELECT \"gr\u{f6}\u{df}e\" FROM orders WHERE \"gr\u{f6}\u{df}e\" IS NOT NULL", - expected_lines: None, + expected_lines: Some(&[ + "succeeded=true star=false", + "gr\u{f6}\u{df}e - unknown", + "gr\u{f6}\u{df}e direct medium [model:orders:gr\u{f6}\u{df}e]", + ]), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs index 0dbac8f6a5..b1f7ddcc37 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs @@ -1,5 +1,23 @@ //! Ordered mappings shared with Python's dicts. +use std::fmt; +use std::sync::Arc; + +/// An adapter's own Python nullability rule: `(declared function name, argument nullabilities)` +/// to the rule's nullability, or the failure the rule raised. +pub type NullabilityRuleFn = + dyn Fn(&str, &[&'static str]) -> Result<&'static str, String> + Send + Sync; + +/// The callback that runs project-local adapter nullability rules, the `python` rule id. +#[derive(Clone)] +pub struct NullabilityCallback(pub Arc); + +impl fmt::Debug for NullabilityCallback { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("NullabilityCallback") + } +} + /// An ordered `name -> value` mapping in Python dict order. pub type Pairs = Vec<(String, String)>; /// Ordered relation shapes in Python dict order. diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs index 184a8b3ff3..9dfbd56c08 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs @@ -213,10 +213,10 @@ fn given_model_sql_when_validating_syntax_then_matches_python_or_defers() { }, SyntaxTestCase { dialect: "trino", - description: "a dialect this parser build does not carry", + description: "a dialect outside the old parser build reads as the wheel does", sql: "SELECT 1 AS a FROM t QUALIFY a = 1", placeholders: &[], - expected_valid: None, + expected_valid: Some(false), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs index feba290be6..7823d39101 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod nullability_rules; pub(crate) mod oracle; pub(crate) mod registration; pub(crate) mod session; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs new file mode 100644 index 0000000000..2c44a25421 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs @@ -0,0 +1,57 @@ +//! Project-local adapter nullability rules, run by calling the adapter's own Python rule. + +use std::sync::{Arc, Mutex, PoisonError}; + +use pyo3::prelude::{Py, PyAny, PyAnyMethods, PyDictMethods, PyErr, Python}; +use pyo3::types::{PyDict, PyTuple}; +use sqlbuild_analysis::assembly::analysis_session::types::NullabilityCallback; + +/// The first exception an adapter rule raised, which the session re-raises to Python. +pub(crate) type RuleFailure = Arc>>; + +const NULLABILITIES: [&str; 3] = ["non_null", "nullable", "unknown"]; +const RULE_RAISED: &str = "an adapter nullability rule raised"; + +/// The callback calling `rules[name](tuple(InferredNullability(value) for value in args))`. +pub(crate) fn nullability_callback( + rules: Py, + nullability: Py, + failure: RuleFailure, +) -> NullabilityCallback { + NullabilityCallback(Arc::new(move |name: &str, arguments: &[&'static str]| { + Python::attach(|python| { + let called = || -> Result<&'static str, PyErr> { + let enum_type = nullability.bind(python); + let rule = rules + .bind(python) + .get_item(name)? + .ok_or_else(|| pyo3::exceptions::PyKeyError::new_err(name.to_owned()))?; + let values = arguments + .iter() + .map(|value| enum_type.call1((*value,))) + .collect::, _>>()?; + let result = rule.call1((PyTuple::new(python, values)?,))?; + let value: String = enum_type.call1((result,))?.getattr("value")?.extract()?; + Ok(NULLABILITIES + .into_iter() + .find(|known| *known == value) + .unwrap_or("unknown")) + }; + called().map_err(|error| { + let mut slot = failure.lock().unwrap_or_else(PoisonError::into_inner); + if slot.is_none() { + *slot = Some(error); + } + RULE_RAISED.to_owned() + }) + }) + })) +} + +/// Take the exception an adapter rule raised, if any. +pub(crate) fn raised(failure: &RuleFailure) -> Option { + failure + .lock() + .unwrap_or_else(PoisonError::into_inner) + .take() +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 0489bfe983..808d0fd741 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -1,6 +1,7 @@ //! The native model analysis session and expression-source shapes for the preview engine. -use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyRef, PyResult, Python}; +use pyo3::prelude::{Bound, Py, PyAny, PyModule, PyModuleMethods, PyRef, PyResult, Python}; +use pyo3::types::PyDict; use pyo3::{pyclass, pyfunction, pymethods, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::main::attach_analysis_cache::attach_analysis_cache; use sqlbuild_analysis::assembly::analysis_session::main::expression_shapes::expression_shapes; @@ -26,6 +27,9 @@ use sqlbuild_cache::store::models::NativeStore; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use std::path::PathBuf; +use crate::bindings::_helpers::analysis_session::nullability_rules::{ + RuleFailure, nullability_callback, raised, +}; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::_helpers::cache::native_store::{open_store, save_store}; use crate::bindings::models::ProjectCatalog; @@ -134,6 +138,7 @@ pub(crate) struct NativeModelAnalysisSession { sharing: (usize, usize), cache_path: Option, cache_stats: Option, + rule_failure: RuleFailure, } /// The native store kind holding finished model analyses. @@ -170,16 +175,23 @@ impl NativeModelAnalysisSession { } /// Advance; None means Python must analyse every model, no deferrals means done. - fn run(&mut self, py: Python<'_>) -> Option { - let mut session: AnalysisSession = self.inner.take()?; + fn run(&mut self, py: Python<'_>) -> PyResult> { + let Some(mut session) = self.inner.take() else { + return Ok(None); + }; let result: Result<(AnalysisSession, SessionStep), String> = py.compiler_detach(move || { run_analysis_session(&mut session).map(|step| (session, step)) }); - let (session, step) = self.kept(result)?; + if let Some(error) = raised(&self.rule_failure) { + return Err(error); + } + let Some((session, step)) = self.kept(result) else { + return Ok(None); + }; self.sharing = session_sharing(&session); self.inner = Some(session); - Some(step_row(step)) + Ok(Some(step_row(step))) } /// Answer the last step's deferrals; False means Python must analyse every model. @@ -244,15 +256,22 @@ impl NativeModelAnalysisSession { } /// Start a session on `catalog`, or None where Python must analyse. +/// +/// `adapter_rules` holds the adapter's own nullability rules by name, with the +/// `InferredNullability` type they take and return; the request names them `python`. #[pyfunction] -#[pyo3(signature = (catalog, request, cache=None))] +#[pyo3(signature = (catalog, request, cache=None, adapter_rules=None))] fn start_model_analysis_session( py: Python<'_>, catalog: PyRef<'_, ProjectCatalog>, request: RequestRow, cache: Option<(PathBuf, String)>, + adapter_rules: Option<(Py, Py)>, ) -> PyResult> { - let request: SessionRequest = session_request(request); + let mut request: SessionRequest = session_request(request); + let rule_failure: RuleFailure = RuleFailure::default(); + request.nullability_callback = adapter_rules + .map(|(rules, nullability)| nullability_callback(rules, nullability, rule_failure.clone())); let catalog = &catalog.inner; let session: Option = py .compiler_detach(|| Ok(start_analysis_session(request, catalog))) @@ -278,6 +297,7 @@ fn start_model_analysis_session( sharing: (0, 0), cache_path, cache_stats, + rule_failure, })) } @@ -329,6 +349,7 @@ fn session_request(request: RequestRow) -> SessionRequest { case_sensitive_shapes, function_return_types, nullability_rules, + nullability_callback: None, rich_type_inference, column_types, column_nullability, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs index ce2bce62cc..59cd249c55 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs @@ -5,9 +5,9 @@ use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::contracts::main::evaluate_model_contracts::evaluate_model_contracts; use sqlbuild_analysis::contracts::main::promotion_conflicts::promotion_conflicts; use sqlbuild_analysis::contracts::models::{ - ContractDiagnostic, ContractModel, ContractRequest, - ContractSchema, ContractSeverity, DeclaredColumn, DeclaredColumnFamily, DynamicContractProof, - InferredOutputColumn, PromotionConflict, PromotionModel, PromotionRequest, + ContractDiagnostic, ContractModel, ContractRequest, ContractSchema, ContractSeverity, + DeclaredColumn, DeclaredColumnFamily, DynamicContractProof, InferredOutputColumn, + PromotionConflict, PromotionModel, PromotionRequest, }; use sqlbuild_analysis::type_system::models::TypeNormalizationError; diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index e38df82be4..1425957d8a 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -460,7 +460,10 @@ class NativeModelAnalysisSession: def cache_stats(self) -> tuple[int, int, int, str | None] | None: ... def start_model_analysis_session( - catalog: object, request: tuple[object, ...], cache: tuple[str, str] | None = None + catalog: object, + request: tuple[object, ...], + cache: tuple[str, str] | None = None, + adapter_rules: tuple[dict[str, Callable[..., object]], type] | None = None, ) -> NativeModelAnalysisSession | None: ... def prove_dynamic_column_contracts( request: tuple[ diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py index 2e15575448..4f4cb3a29c 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py @@ -5,10 +5,15 @@ from typing import Any import sqlbuild._native as _native +from sqlbuild.adapter.contract.types import FunctionNullabilityRule from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral -from sqlbuild.compiler.analysis_session._helpers.session_rows import session_request +from sqlbuild.compiler.analysis_session._helpers.session_rows import ( + adapter_nullability_rules, + session_request, +) from sqlbuild.compiler.analysis_session.classes.native_model_analysis import NativeModelAnalysis from sqlbuild.compiler.analysis_session.constants import ( + ADAPTER_NULLABILITY_CALLBACK, DEFERRAL_NO_CATALOG, DEFERRAL_NO_COMPACT_ANALYSIS, DEFERRAL_SESSION, @@ -22,6 +27,7 @@ from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis from sqlbuild.compiler.compile.models import AnalysisCacheContext, ModelSqlAnalysis from sqlbuild.compiler.frontier.main.compiled_code_identity import compiled_code_identity +from sqlbuild.compiler.lineage.types import InferredNullability def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses | None: @@ -44,8 +50,18 @@ def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModel row: tuple[object, ...] | None = session_request( request=request, python=python, schemas=catalog.schemas ) + adapter_rules: dict[str, FunctionNullabilityRule] = adapter_nullability_rules( + request.inference_profile + ) + if adapter_rules: + record_analysis_deferral(kind=ADAPTER_NULLABILITY_CALLBACK) session: _native.NativeModelAnalysisSession | None = ( - _native.start_model_analysis_session(catalog.native, row, _analysis_store(request)) + _native.start_model_analysis_session( + catalog.native, + row, + None if adapter_rules else _analysis_store(request), + (adapter_rules, InferredNullability) if adapter_rules else None, + ) if row is not None else None ) diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 99cd11b33f..3b2cc41f3d 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -6,13 +6,16 @@ from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.contract.types import FunctionNullabilityRule +from sqlbuild.adapter.type_system.main._safe_cast_nullability import safe_cast_nullability from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( conditional_result_nullability, ) from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability from sqlbuild.compiler.analysis_session.constants import ( + NULLABILITY_RULE_ADAPTER, NULLABILITY_RULE_CONDITIONAL_RESULT, NULLABILITY_RULE_FIRST_ARG, + NULLABILITY_RULE_SAFE_CAST, ) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( @@ -42,6 +45,7 @@ _NULLABILITY_RULE_IDS: dict[FunctionNullabilityRule, str] = { first_arg_nullability: NULLABILITY_RULE_FIRST_ARG, conditional_result_nullability: NULLABILITY_RULE_CONDITIONAL_RESULT, + safe_cast_nullability: NULLABILITY_RULE_SAFE_CAST, } @@ -88,16 +92,25 @@ def session_request( ) -def nullability_rule_rows(profile: ExpressionInferenceProfile) -> list[tuple[str, str]] | None: - """Adapter nullability rules as `(name, rule id)`, or None for a rule SQLBuild lacks.""" +def nullability_rule_rows(profile: ExpressionInferenceProfile) -> list[tuple[str, str]]: + """Adapter nullability rules as `(name, rule id)`; an adapter's own rule is `python`.""" - rows: list[tuple[str, str]] = [] - for name, rule in profile.function_nullability_rules.items(): - rule_id: str | None = _NULLABILITY_RULE_IDS.get(rule) - if rule_id is None: - return None - rows.append((name, rule_id)) - return rows + return [ + (name, _NULLABILITY_RULE_IDS.get(rule, NULLABILITY_RULE_ADAPTER)) + for name, rule in profile.function_nullability_rules.items() + ] + + +def adapter_nullability_rules( + profile: ExpressionInferenceProfile, +) -> dict[str, FunctionNullabilityRule]: + """The adapter's own nullability rules, which the native session calls back into.""" + + return { + name: rule + for name, rule in profile.function_nullability_rules.items() + if rule not in _NULLABILITY_RULE_IDS + } def contract_proof(row: ProofRow | None) -> DynamicColumnContractProof | None: diff --git a/src/sqlbuild/compiler/analysis_session/constants.py b/src/sqlbuild/compiler/analysis_session/constants.py index 265104358b..72600f5339 100644 --- a/src/sqlbuild/compiler/analysis_session/constants.py +++ b/src/sqlbuild/compiler/analysis_session/constants.py @@ -18,6 +18,9 @@ NATIVE_SHAPES_FAILURE_MESSAGE: str = "native expression-source shape inference failed; falling back" NULLABILITY_RULE_FIRST_ARG: str = "first_arg" NULLABILITY_RULE_CONDITIONAL_RESULT: str = "conditional_result" +NULLABILITY_RULE_SAFE_CAST: str = "safe_cast" +NULLABILITY_RULE_ADAPTER: str = "python" +ADAPTER_NULLABILITY_CALLBACK: str = "adapter_nullability_callback" NATIVE_ANALYSIS_STORE_FILE_NAME: str = "model-analyses.bin" NATIVE_ANALYSIS_STORE_VERSION: str = "native-model-analysis-store-v1" NATIVE_ANALYSIS_STORE_FAILURE_MESSAGE: str = "native model analysis cache not read or saved" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index f6cc2c9610..18a19223ca 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -60,16 +60,6 @@ class CyclicCompileTestCase: expected_compiled: tuple[str, ...] -@dataclass(frozen=True) -class SessionFailureTestCase: - """A session that fails after Python answered deferrals, so Python analyses everything.""" - - description: str - seed: int - model_count: int - expected_kinds: dict[str, int] - - @dataclass(frozen=True) class StandalonePivotProofTestCase: """A pivot model left out of model analysis, whose proof assembly takes natively.""" @@ -105,3 +95,14 @@ class CteFactRecoveryParityTestCase: count: int expected_minimum_compared: int expected_minimum_recovered: dict[str, int] + + +@dataclass(frozen=True) +class AdapterRuleCallbackTestCase: + """A project-local adapter rule the native session calls back into, and what it raises.""" + + description: str + seed: int + model_count: int + raised: type[Exception] | None + expected_minimum_calls: int diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 0b4f769781..c22c15a1a7 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -506,10 +506,8 @@ def started_sessions(*, monkeypatch: pytest.MonkeyPatch) -> list[Any]: started: list[Any] = [] original: Callable[..., Any] = native_module.start_model_analysis_session - def start( - catalog: object, request: tuple[object, ...], cache: tuple[str, str] | None = None - ) -> Any: - started.append(original(catalog, request, cache)) + def start(catalog: object, request: tuple[object, ...], *options: object) -> Any: + started.append(original(catalog, request, *options)) return started[-1] monkeypatch.setattr(native_module, "start_model_analysis_session", start) @@ -829,38 +827,3 @@ def _analysis_views(analyses: dict[str, ModelSqlAnalysis] | None) -> object: def _catalog_view(catalog: Any) -> tuple[object, ...]: return (dict(catalog.schemas), dict(catalog.analysis_shapes)) - - -class FailingProvideSession: - """A native session whose answers to deferrals are refused, as a failed session refuses.""" - - def __init__(self, session: Any) -> None: - self._session: Any = session - self.answered: int = 0 - self.failure: str = "injected session failure" - - def run(self) -> object: - return self._session.run() - - def provide(self, answers: list[object]) -> bool: - self.answered += len(answers) - return False - - def finish(self) -> object: - return self._session.finish() - - -def failing_provide_sessions(*, monkeypatch: pytest.MonkeyPatch) -> list[FailingProvideSession]: - """Make every native session refuse Python's answers; return the sessions started.""" - - started: list[FailingProvideSession] = [] - original: Callable[..., Any] = native_module.start_model_analysis_session - - def start( - catalog: object, request: tuple[object, ...], cache: tuple[str, str] | None = None - ) -> FailingProvideSession: - started.append(FailingProvideSession(original(catalog, request, cache))) - return started[-1] - - monkeypatch.setattr(native_module, "start_model_analysis_session", start) - return started diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 518e9f3fc7..44436feba6 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -25,21 +25,20 @@ ) from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine -from sqlbuild.compiler.lineage.types import ColumnLineageMode +from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + AdapterRuleCallbackTestCase, AnalysisFallbackTestCase, CyclicAnalysisTestCase, CyclicCompileTestCase, GeneratedAnalysisParityTestCase, - SessionFailureTestCase, SharedAnalysisTestCase, StandalonePivotProofTestCase, ) from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( ADAPTER_RULE_MODELS, AnalysisParity, - FailingProvideSession, NativePivotProofs, analysis_request, compare_analyses, @@ -47,7 +46,6 @@ compiled_project_view, custom_nullability_rule, deferral_kinds, - failing_provide_sessions, generated_analysis_files, native_pivot_proofs, pivot_project_files, @@ -65,6 +63,16 @@ SnowflakeAdapter().expression_inference_profile(), DuckDbAdapter().expression_inference_profile(), ) +_CUSTOM_RULE_PROFILES: tuple[ExpressionInferenceProfile, ...] = ( + ExpressionInferenceProfile( + sql_analysis_dialect="duckdb", + function_nullability_rules={ + **DuckDbAdapter().expression_inference_profile().function_nullability_rules, + "SUM": custom_nullability_rule, + "COALESCE": custom_nullability_rule, + }, + ), +) _CYCLIC_PROJECT: dict[str, str] = { "sqlbuild_project.toml": 'name = "orders_cycle"\nadapter = "duckdb"\n', "sources/raw.yml": ( @@ -148,7 +156,7 @@ expected_minimum_pivot_proofs=30, expected_minimum_python_cte_recoveries=4, expected_minimum_legacy_analyses=52, - expected_legacy_analysis_deferrals=6, + expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=10, expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=30, @@ -166,11 +174,29 @@ expected_minimum_pivot_proofs=30, expected_minimum_python_cte_recoveries=4, expected_minimum_legacy_analyses=62, - expected_legacy_analysis_deferrals=18, + expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=10, expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=20, ), + GeneratedAnalysisParityTestCase( + description="a project-local adapter's own nullability rules, called back natively", + seed=20261011, + count=3, + model_count=24, + inference_profiles=_CUSTOM_RULE_PROFILES, + extra_files=ADAPTER_RULE_MODELS, + lineage_mode=ColumnLineageMode.FAST, + expected_minimum_native=1, + expected_minimum_expression_shapes=1, + expected_minimum_pivot_proofs=1, + expected_minimum_python_cte_recoveries=0, + expected_minimum_legacy_analyses=1, + expected_legacy_analysis_deferrals=0, + expected_minimum_proven_pivots=1, + expected_maximum_enrichment_deferrals=0, + expected_minimum_native_enrichments=1, + ), ], ids=lambda case: case.description, ) @@ -430,45 +456,6 @@ def test_given_unsupported_analysis_when_analysing_natively_then_python_analyses assert deferral_kinds(record_dir) == Counter({f"analysis_session:{test_case.expected_kind}": 1}) -@pytest.mark.parametrize( - "test_case", - [ - SessionFailureTestCase( - description="a custom adapter rule defers analyses before the session fails", - seed=20261009, - model_count=12, - expected_kinds={"analysis_session:session": 1}, - ) - ], - ids=lambda case: case.description, -) -def test_given_session_failure_after_deferrals_when_analysing_then_records_only_the_session( - test_case: SessionFailureTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - record_dir: Path = tmp_path / "records" - request: NativeModelAnalysisRequest = analysis_request( - inputs=compile_inputs( - project_dir=tmp_path / "project", - files=generated_analysis_files( - rng=random.Random(test_case.seed), model_count=test_case.model_count - ), - ), - monkeypatch=monkeypatch, - inference_profile=ExpressionInferenceProfile( - sql_analysis_dialect="duckdb", - function_nullability_rules={"SUM": custom_nullability_rule}, - ), - ) - sessions: list[FailingProvideSession] = failing_provide_sessions(monkeypatch=monkeypatch) - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) - - analyses: object = analyze_native_model_sql(request=request) - - assert analyses is None - assert sum(session.answered for session in sessions) > 0 - assert deferral_kinds(record_dir) == Counter(test_case.expected_kinds) - - @pytest.mark.parametrize( "test_case", [ @@ -528,5 +515,71 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with ) +@pytest.mark.parametrize( + "test_case", + [ + AdapterRuleCallbackTestCase( + description="the adapter's own rule answers natively analysed CTE nullability", + seed=20261012, + model_count=6, + raised=None, + expected_minimum_calls=1, + ), + AdapterRuleCallbackTestCase( + description="the adapter rule's exception reaches the caller unchanged", + seed=20261012, + model_count=6, + raised=LookupError, + expected_minimum_calls=1, + ), + ], + ids=lambda case: case.description, +) +def test_given_adapter_nullability_rule_when_analysing_natively_then_rule_is_called_back( + test_case: AdapterRuleCallbackTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + calls: list[tuple[InferredNullability, ...]] = [] + raised: list[type[Exception]] = [] + + def adapter_rule(arguments: tuple[InferredNullability, ...]) -> InferredNullability: + calls.append(arguments) + if raised: + raise raised[0]("adapter rule failed") + return InferredNullability.NULLABLE + + request: NativeModelAnalysisRequest = analysis_request( + inputs=compile_inputs( + project_dir=tmp_path / "project", + files={ + **generated_analysis_files( + rng=random.Random(test_case.seed), model_count=test_case.model_count + ), + **ADAPTER_RULE_MODELS, + }, + ), + monkeypatch=monkeypatch, + inference_profile=ExpressionInferenceProfile( + sql_analysis_dialect="duckdb", + function_nullability_rules={"UPPER": adapter_rule, "IFF": adapter_rule}, + ), + ) + calls.clear() + raised.extend(filter(None, (test_case.raised,))) + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + + if test_case.raised is None: + assert analyze_native_model_sql(request=request) is not None + else: + with pytest.raises(test_case.raised, match="adapter rule failed"): + _ = analyze_native_model_sql(request=request) + + assert len(calls) >= test_case.expected_minimum_calls, test_case.description + assert all(isinstance(value, InferredNullability) for args in calls for value in args) + assert deferral_kinds(record_dir) == Counter( + {"analysis_session:adapter_nullability_callback": 1} + ), test_case.description + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From 20140df9e4d4686e960d5a512bd87df0ad6c1cfc Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:43:43 +0100 Subject: [PATCH 08/36] feat(native): build FAST lineage natively for every dialect and restored projects Unknown dialect names raise Python's ValueError, projects without an analysis catalog run on a fresh analysis pool, and column references are walked iteratively. Deletes the whole-project Python FAST lineage fallback; the per-model handback for native failures stays. --- .../tests/test_analysis_session.rs | 10 +-- .../src/lineage/_helpers/parsed_lineage.rs | 8 +- .../src/lineage/_helpers/projections.rs | 59 ++++++-------- .../src/lineage/main/build_fast_lineage.rs | 18 ++++- .../sqlbuild-analysis/src/lineage/models.rs | 6 +- .../src/lineage/tests/helpers.rs | 2 +- .../lineage/tests/test_build_fast_lineage.rs | 14 ++-- .../semantic_validation/_helpers/catalog.rs | 19 +++-- .../bindings/_helpers/lineage/fast_lineage.rs | 7 +- src/sqlbuild/_native.pyi | 2 +- .../compiler/lineage/_helpers/fast_columns.py | 80 +------------------ .../lineage/_helpers/native_fast_columns.py | 15 ++-- src/sqlbuild/compiler/lineage/constants.py | 3 +- .../main/_build_native_column_lineage.py | 2 +- src/sqlbuild/compiler/lineage/main/columns.py | 12 +-- .../sqlbuild/compiler/lineage/_test_types.py | 8 +- .../src/sqlbuild/compiler/lineage/helpers.py | 21 ++--- .../lineage/test_native_fast_lineage.py | 63 ++++++++------- 18 files changed, 127 insertions(+), 222 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 6bd8c48051..cc144aa743 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -122,12 +122,10 @@ fn given_models_when_running_the_session_then_defers_and_publishes_as_python_doe &["events"], ), ], - expected_steps: &[ - &[ - "publish events gr\u{f6}\u{df}e:UNKNOWN", - "publish events_mart gr\u{f6}\u{df}e:UNKNOWN", - ], - ], + expected_steps: &[&[ + "publish events gr\u{f6}\u{df}e:UNKNOWN", + "publish events_mart gr\u{f6}\u{df}e:UNKNOWN", + ]], expected_outcomes: &[ &[ "gr\u{f6}\u{df}e - unknown", diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs index 55e9557c5f..252bed3b64 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs @@ -30,13 +30,15 @@ pub(crate) struct ParsedModel<'a> { pub(crate) query_sql: &'a str, pub(crate) inferred_names: &'a [String], pub(crate) schema: &'a HashMap>, - pub(crate) dialect: Option, + /// The parser dialect, or the dialect name Polyglot does not know. + pub(crate) dialect: Result, pub(crate) options: &'a Result, } pub(crate) fn parsed_model_lineage(model: &ParsedModel<'_>) -> FastLineageOutcome { - let Some(dialect) = model.dialect else { - return FastLineageOutcome::Deferred(LineageDeferral::UnsupportedDialect); + let dialect: DialectType = match model.dialect { + Ok(dialect) => dialect, + Err(name) => return FastLineageOutcome::UnknownDialect(name.to_owned()), }; let Ok(options) = model.options else { return FastLineageOutcome::Deferred(LineageDeferral::NativeFailure); diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs index d13bb561dc..c289d21b22 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs @@ -15,9 +15,6 @@ use crate::lineage::models::{ LineageConfidence, LineageResourceType, LineageSource, LineageTransformKind, }; -/// Payload nesting past which Python's recursive `to_dict` walk may exceed its recursion limit. -const MAX_PAYLOAD_DEPTH: usize = 400; - /// A read Python would make differently, or fail on, so the model is deferred to Python. #[derive(Debug)] pub(crate) struct UnreadableExpression; @@ -265,41 +262,35 @@ fn column_refs(expression: &Expression) -> Result, Unreada }; return Ok(vec![(expression.get_name().to_owned(), table_name)]); } - payload_column_refs(&payload, 0) + Ok(payload_column_refs(&payload)) } -/// Python's recursive `visit`: a `column` dict ends its branch; tuples are never entered. -fn payload_column_refs( - node: &PythonValue, - depth: usize, -) -> Result, UnreadableExpression> { - if depth > MAX_PAYLOAD_DEPTH { - return Err(UnreadableExpression); - } - let children: Vec<&PythonValue> = match node { - PythonValue::Dict(entries) => { - if let Some(column @ PythonValue::Dict(_)) = node.get(PAYLOAD_COLUMN) { - let table_name = match column.get(PAYLOAD_TABLE) { - Some(table @ PythonValue::Dict(_)) => { - name_payload_value(table.get(PAYLOAD_NAME)) - } - _ => String::new(), - }; - return Ok(vec![( - name_payload_value(column.get(PAYLOAD_NAME)), - table_name, - )]); - } - entries.iter().map(|(_, value)| value).collect() - } - PythonValue::List(values) => values.iter().collect(), - _ => Vec::new(), - }; +/// Python's recursive `visit`, depth first in payload order: a `column` dict ends its branch; +/// tuples are never entered. +fn payload_column_refs(root: &PythonValue) -> Vec<(String, String)> { let mut refs: Vec<(String, String)> = Vec::new(); - for child in children { - refs.extend(payload_column_refs(child, depth + 1)?); + let mut pending: Vec<&PythonValue> = vec![root]; + while let Some(node) = pending.pop() { + let children: Vec<&PythonValue> = match node { + PythonValue::Dict(entries) => { + if let Some(column @ PythonValue::Dict(_)) = node.get(PAYLOAD_COLUMN) { + let table_name = match column.get(PAYLOAD_TABLE) { + Some(table @ PythonValue::Dict(_)) => { + name_payload_value(table.get(PAYLOAD_NAME)) + } + _ => String::new(), + }; + refs.push((name_payload_value(column.get(PAYLOAD_NAME)), table_name)); + continue; + } + entries.iter().map(|(_, value)| value).collect() + } + PythonValue::List(values) => values.iter().collect(), + _ => Vec::new(), + }; + pending.extend(children.into_iter().rev()); } - Ok(refs) + refs } /// Python's `_polyglot_name_payload_value`. diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index 4eeb8c3c90..2aabc91905 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -16,21 +16,31 @@ use crate::lineage::main::parser_dialect::parser_dialect; use crate::lineage::models::{ FastLineageModel, FastLineageOutcome, FastLineageRequest, LineageDeferral, }; +use crate::semantic_validation::_helpers::catalog::new_analysis_pool; use crate::semantic_validation::models::ProjectCatalog; -/// One outcome per model, in request order, run on the compile's deep-stack analysis pool. +/// One outcome per model, in request order, run on the compile's deep-stack analysis pool, or +/// on a fresh one for a project restored without its catalog. pub fn build_fast_lineage( request: &FastLineageRequest, - catalog: &ProjectCatalog, + catalog: Option<&ProjectCatalog>, ) -> Result, String> { - let pool: Arc = catalog.analysis_pool()?; + let pool: Arc = match catalog { + Some(catalog) => catalog.analysis_pool()?, + None => Arc::new(new_analysis_pool()?), + }; Ok(pool.install(|| model_outcomes(request))) } /// A parser panic defers only its own model. fn model_outcomes(request: &FastLineageRequest) -> Vec { let schema = schema_mapping(&request.schema); - let dialect = parser_dialect(request.dialect.as_deref()); + let name: &str = request + .dialect + .as_deref() + .filter(|name| !name.is_empty()) + .unwrap_or("generic"); + let dialect = parser_dialect(Some(name)).ok_or(name); let options: Result = proxy_parse_options(); request .models diff --git a/crates/sqlbuild-analysis/src/lineage/models.rs b/crates/sqlbuild-analysis/src/lineage/models.rs index 7076c5dcaa..8f82026308 100644 --- a/crates/sqlbuild-analysis/src/lineage/models.rs +++ b/crates/sqlbuild-analysis/src/lineage/models.rs @@ -121,8 +121,6 @@ pub struct LineageColumn { /// Why a model is handed back to Python, which builds its lineage exactly as before. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LineageDeferral { - /// The dialect is not compiled into the native parser. - UnsupportedDialect, /// The parsed SQL could not be read as Python reads it. NativeFailure, } @@ -130,7 +128,6 @@ pub enum LineageDeferral { impl LineageDeferral { pub fn as_str(self) -> &'static str { match self { - Self::UnsupportedDialect => "unsupported_dialect", Self::NativeFailure => "native_failure", } } @@ -149,6 +146,8 @@ pub enum FastLineageOutcome { Omitted, /// The SQL did not parse; Python logs this message and records no lineage. Unparsed(String), + /// Polyglot does not know the dialect name, so Python's parse raises `ValueError`. + UnknownDialect(String), Deferred(LineageDeferral), } @@ -160,6 +159,7 @@ impl FastLineageOutcome { Self::Built { columns, has_star } => ("built", columns, has_star, None), Self::Omitted => ("omitted", Vec::new(), false, None), Self::Unparsed(message) => ("unparsed", Vec::new(), false, Some(message)), + Self::UnknownDialect(name) => ("unknown_dialect", Vec::new(), false, Some(name)), Self::Deferred(kind) => ( "deferred", Vec::new(), diff --git a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs index c375293a7a..b5699ce9ba 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs @@ -22,7 +22,7 @@ pub(crate) fn outcome_lines( models: vec![model], }; let outcomes: Vec = - build_fast_lineage(&request, &catalog()).expect("the analysis pool builds"); + build_fast_lineage(&request, Some(&catalog())).expect("the analysis pool builds"); outcomes.into_iter().map(described_outcome).collect() } diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index c166eb7c97..424b3318fa 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -206,24 +206,24 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( expected_detail: Some("native_failure"), }, ParsedLineageTestCase { - description: "a dialect outside this parser build is deferred", + description: "a dialect outside the old parser build is parsed natively", dialect: Some("mysql"), sql: "SELECT 1", inferred_columns: &[], - expected_status: "deferred", - expected_lines: &[], + expected_status: "built", + expected_lines: &["1 constant high []"], expected_has_star: false, - expected_detail: Some("unsupported_dialect"), + expected_detail: None, }, ParsedLineageTestCase { - description: "an unknown dialect is deferred", + description: "an unknown dialect raises Python's error", dialect: Some("not-a-dialect"), sql: "SELECT 1", inferred_columns: &[], - expected_status: "deferred", + expected_status: "unknown_dialect", expected_lines: &[], expected_has_star: false, - expected_detail: Some("unsupported_dialect"), + expected_detail: Some("not-a-dialect"), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs index 3ce72f609a..e166e32913 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs @@ -310,14 +310,7 @@ impl ProjectCatalog { if let Some(pool) = pool.as_ref() { return Ok(Arc::clone(pool)); } - let built = Arc::new( - rayon::ThreadPoolBuilder::new() - .num_threads(ANALYSIS_WORKERS) - .stack_size(ANALYSIS_WORKER_STACK_BYTES) - .thread_name(|index| format!("sqlbuild-analysis-{index}")) - .build() - .map_err(|error| error.to_string())?, - ); + let built = Arc::new(new_analysis_pool()?); *pool = Some(Arc::clone(&built)); Ok(built) } @@ -615,3 +608,13 @@ fn has_exact_columns(table: &SchemaTable) -> bool { .iter() .any(|column| column.name.starts_with('"')) } + +/// A deep-stack analysis pool, for work that runs without a compile's catalog. +pub(crate) fn new_analysis_pool() -> Result { + rayon::ThreadPoolBuilder::new() + .num_threads(ANALYSIS_WORKERS) + .stack_size(ANALYSIS_WORKER_STACK_BYTES) + .thread_name(|index| format!("sqlbuild-analysis-{index}")) + .build() + .map_err(|error| error.to_string()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/lineage/fast_lineage.rs b/crates/sqlbuild-python/src/bindings/_helpers/lineage/fast_lineage.rs index 7d20c1eba4..1bea7093f1 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/lineage/fast_lineage.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/lineage/fast_lineage.rs @@ -25,11 +25,11 @@ type LineageRequestInput = ( /// `(status, columns, has_star, detail)`; see `FastLineageOutcome::into_parts`. type OutcomeRow = (&'static str, Vec, bool, Option); -/// Build fast lineage for the requested models on `catalog`'s analysis pool. +/// Build fast lineage for the requested models on `catalog`'s analysis pool, if any. #[pyfunction] fn build_fast_column_lineage( py: Python<'_>, - catalog: PyRef<'_, ProjectCatalog>, + catalog: Option>, request: LineageRequestInput, ) -> PyResult> { let (dialect, schema, models) = request; @@ -43,7 +43,8 @@ fn build_fast_column_lineage( schema, models, }; - let catalog: &validation::ProjectCatalog = &catalog.inner; + let catalog: Option<&validation::ProjectCatalog> = + catalog.as_ref().map(|catalog| &catalog.inner); let outcomes: Vec = py .compiler_detach(|| build_fast_lineage(&request, catalog)) .map_err(compiler_error)?; diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 1425957d8a..e4d6000426 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -649,7 +649,7 @@ def native_promotion_conflicts( # Native analysis: column lineage facts. def build_fast_column_lineage( - catalog: object, + catalog: object | None, request: tuple[str | None, list[tuple[str, str, list[str]]], list[tuple[bool, str, list[str]]]], /, ) -> list[tuple[str, list[tuple[str, str, str, list[tuple[str, str, str]]]], bool, str | None]]: ... diff --git a/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py b/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py index aa941ff376..d70a7b25c4 100644 --- a/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py +++ b/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py @@ -1,20 +1,15 @@ -"""Fast Polyglot-backed column lineage analyzer.""" +"""Python fast lineage of one model, for models the native engine hands back.""" from __future__ import annotations import logging -from collections.abc import Sequence from typing import Any, cast from sqlbuild.compiler.compile.models import ( - CompiledLineageColumnFact, - CompiledLineageSourceFact, CompiledModel, - CompiledProject, ) from sqlbuild.compiler.compile.types import CompiledResourceType from sqlbuild.compiler.lineage._helpers.columns import ( - _build_schema_mapping, _build_star_lineage, _normalize_sqlbuild_refs, ) @@ -24,7 +19,6 @@ ColumnLineageSource, ModelColumnLineage, PhysicalResource, - ProjectColumnLineage, ) from sqlbuild.compiler.lineage.types import ( ColumnLineageConfidence, @@ -47,78 +41,6 @@ _DEBUG_LOGGER: logging.Logger = logging.getLogger("sqlbuild.lineage") -def build_fast_project_column_lineage( - *, - project: CompiledProject, - dialect: str | None = None, - model_names: frozenset[str] | None = None, -) -> ProjectColumnLineage | None: - """Build a fast, partial project column lineage graph for compiled models.""" - - if not project.settings.sql_analysis: - return None - - schema: dict[str, dict[str, str]] = _build_schema_mapping(project) - model_results: dict[str, ModelColumnLineage] = {} - compact_models: dict[str, tuple[Sequence[CompiledLineageColumnFact], bool]] = {} - model_order: list[str] = [] - - for model in project.models: - if model_names is not None and model.name not in model_names: - continue - if model.fast_lineage_columns is not None: - columns: Sequence[CompiledLineageColumnFact] = model.fast_lineage_columns - if model.fast_lineage_has_star and not model.fast_lineage_star_resolved: - normalized_sql: str - physical_resources: tuple[PhysicalResource, ...] - normalized_sql, physical_resources = _normalize_sqlbuild_refs(model.query_sql) - del normalized_sql - star_columns: tuple[ColumnLineage, ...] = _build_star_lineage( - model=model, - schema=schema, - physical_resources=physical_resources, - existing_columns={column.output_column for column in columns}, - ) - columns = ( - *columns, - *(_compiled_lineage_fact(column) for column in star_columns), - ) - compact_models[model.name] = (columns, model.fast_lineage_has_star) - model_order.append(model.name) - continue - result: ModelColumnLineage | None = _build_polyglot_fast_model_column_lineage( - model=model, - schema=schema, - dialect=dialect, - ) - if result is None: - continue - model_results[model.name] = result - model_order.append(model.name) - - return ProjectColumnLineage.from_fast_facts( - models=model_results, - compact_models=compact_models, - model_order=tuple(model_order), - ) - - -def _compiled_lineage_fact(column: ColumnLineage) -> CompiledLineageColumnFact: - return CompiledLineageColumnFact( - output_column=column.output_column, - upstream_columns=tuple( - CompiledLineageSourceFact( - resource_type=source.resource_type, - resource_name=source.resource_name, - column_name=source.column_name, - ) - for source in column.upstream_columns - ), - transform_kind=column.transform_kind, - confidence=column.confidence, - ) - - def _build_polyglot_fast_model_column_lineage( *, model: CompiledModel, diff --git a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py index 61895dfc95..746c185c81 100644 --- a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py +++ b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py @@ -22,8 +22,7 @@ from sqlbuild.compiler.lineage.constants import ( NATIVE_LINEAGE_BUILT, NATIVE_LINEAGE_DEFERRED, - NATIVE_LINEAGE_NO_CATALOG, - NATIVE_LINEAGE_STAR, + NATIVE_LINEAGE_UNKNOWN_DIALECT, NATIVE_LINEAGE_UNPARSED, ) from sqlbuild.compiler.lineage.models import ( @@ -51,7 +50,7 @@ def build_native_fast_project_column_lineage( dialect: str | None, model_names: frozenset[str] | None, ) -> ProjectColumnLineage | None: - """Build `build_fast_project_column_lineage`'s graph with native star expansion and parsing.""" + """Build the fast lineage graph with native star expansion and parsing.""" if not project.settings.sql_analysis: return None @@ -66,10 +65,6 @@ def build_native_fast_project_column_lineage( catalog: object | None = ( project.binding_catalog.native if project.binding_catalog is not None else None ) - if catalog is None and requests: - for _ in requests: - record_lineage_deferral(kind=NATIVE_LINEAGE_NO_CATALOG) - return None outcomes: list[_NativeOutcome] = ( _native.build_fast_column_lineage(catalog, (dialect, _schema_resources(project), requests)) if requests @@ -84,10 +79,8 @@ def build_native_fast_project_column_lineage( if model.fast_lineage_columns is not None: columns: Sequence[CompiledLineageColumnFact] = model.fast_lineage_columns if _needs_star_expansion(model): - status, star_columns, _, _ = outcomes[outcome_index] + _, star_columns, _, _ = outcomes[outcome_index] outcome_index += 1 - if status != NATIVE_LINEAGE_STAR: - return None columns = (*columns, *(_column_fact(column) for column in star_columns)) compact_models[model.name] = (columns, model.fast_lineage_has_star) model_order.append(model.name) @@ -108,6 +101,8 @@ def build_native_fast_project_column_lineage( sqlbuild_model=model.name, sqlbuild_error=detail, ) + elif status == NATIVE_LINEAGE_UNKNOWN_DIALECT: + raise ValueError(f"Unknown dialect: {detail}") elif status == NATIVE_LINEAGE_DEFERRED: record_lineage_deferral(kind=str(detail)) if python_schema is None: diff --git a/src/sqlbuild/compiler/lineage/constants.py b/src/sqlbuild/compiler/lineage/constants.py index f6f49d295b..528a7bd66a 100644 --- a/src/sqlbuild/compiler/lineage/constants.py +++ b/src/sqlbuild/compiler/lineage/constants.py @@ -3,10 +3,9 @@ STAR_COLUMN_NAME: str = "*" POLYGLOT_RESOLVED_SOURCE_CONFIDENCE: str = "resolved" INDEXED_EDGE_RECORD_LENGTH: int = 6 -NATIVE_LINEAGE_STAR: str = "star" NATIVE_LINEAGE_BUILT: str = "built" NATIVE_LINEAGE_UNPARSED: str = "unparsed" NATIVE_LINEAGE_DEFERRED: str = "deferred" -NATIVE_LINEAGE_NO_CATALOG: str = "no_analysis_catalog" +NATIVE_LINEAGE_UNKNOWN_DIALECT: str = "unknown_dialect" NATIVE_LINEAGE_DEFERRAL_SITE: str = "fast_columns.py" ANALYSIS_DEFERRAL_RECORD_PREFIX: str = "analysis-deferrals-" diff --git a/src/sqlbuild/compiler/lineage/main/_build_native_column_lineage.py b/src/sqlbuild/compiler/lineage/main/_build_native_column_lineage.py index d5acf51a0d..b41d6b2555 100644 --- a/src/sqlbuild/compiler/lineage/main/_build_native_column_lineage.py +++ b/src/sqlbuild/compiler/lineage/main/_build_native_column_lineage.py @@ -12,7 +12,7 @@ def build_native_column_lineage( *, project: CompiledProject, dialect: str | None, model_names: frozenset[str] | None ) -> ProjectColumnLineage | None: - """Return the fast lineage graph, or None where Python must build it.""" + """Return the fast lineage graph, or None without SQL analysis.""" return build_native_fast_project_column_lineage( project=project, dialect=dialect, model_names=model_names diff --git a/src/sqlbuild/compiler/lineage/main/columns.py b/src/sqlbuild/compiler/lineage/main/columns.py index 6bb3079ca7..5a421f3e3f 100644 --- a/src/sqlbuild/compiler/lineage/main/columns.py +++ b/src/sqlbuild/compiler/lineage/main/columns.py @@ -3,9 +3,6 @@ from __future__ import annotations from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.lineage._helpers.fast_columns import ( - build_fast_project_column_lineage, -) from sqlbuild.compiler.lineage._helpers.rich_columns import ( build_rich_project_column_lineage, ) @@ -27,16 +24,9 @@ def build_project_column_lineage( match mode: case ColumnLineageMode.FAST: - native_lineage: ProjectColumnLineage | None = build_native_column_lineage( + return build_native_column_lineage( project=project, dialect=dialect, model_names=model_names ) - if native_lineage is not None: - return native_lineage - return build_fast_project_column_lineage( - project=project, - dialect=dialect, - model_names=model_names, - ) case ColumnLineageMode.RICH: return build_rich_project_column_lineage( project=project, diff --git a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py index e56e9b4f5e..5089f90443 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py @@ -5,7 +5,7 @@ @dataclass(frozen=True) class GeneratedLineageParityTestCase: - """Seeded generated projects whose fast lineage Python and the native engine must agree on.""" + """Seeded generated projects whose fast lineage must not depend on the analysis catalog.""" description: str seed: int @@ -17,14 +17,14 @@ class GeneratedLineageParityTestCase: @dataclass(frozen=True) -class DeferredLineageTestCase: - """Parsed models the native engine hands back to Python, which records each deferral.""" +class FormerlyDeferredLineageTestCase: + """Parsed models Python once built, now answered natively or raising Python's error.""" description: str dialect: str keeps_catalog: bool expected_native_statuses: dict[str, int] - expected_kind: str + expected_error: str | None @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/lineage/helpers.py b/tests/integration/src/sqlbuild/compiler/lineage/helpers.py index 4a6af823e5..913cdb4b31 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/helpers.py @@ -14,7 +14,6 @@ from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.discovery.main.discover import discover_project_inputs -from sqlbuild.compiler.lineage._helpers.fast_columns import build_fast_project_column_lineage from sqlbuild.compiler.lineage.main._build_native_column_lineage import ( build_native_column_lineage, ) @@ -236,24 +235,14 @@ def lineage_views( project: CompiledProject, dialect: str | None, model_names: frozenset[str] | None, -) -> tuple[list[object], list[object], list[object]]: - """Each public lineage view's name, Python's value and the native stage's value.""" +) -> tuple[list[object], list[object]]: + """Each public view of the native fast lineage: its name and value.""" - python: ProjectColumnLineage = cast( - ProjectColumnLineage, - build_fast_project_column_lineage( - project=project, dialect=dialect, model_names=model_names - ), - ) - native: ProjectColumnLineage = cast( + lineage: ProjectColumnLineage = cast( ProjectColumnLineage, - build_native_column_lineage(project=project, dialect=dialect, model_names=model_names) - or build_fast_project_column_lineage( - project=project, dialect=dialect, model_names=model_names - ), + build_native_column_lineage(project=project, dialect=dialect, model_names=model_names), ) - names, python_values = _public_views(project=project, lineage=python) - return names, python_values, _public_views(project=project, lineage=native)[1] + return _public_views(project=project, lineage=lineage) def _public_views( diff --git a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py index 9894d30670..125401e8e5 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py @@ -11,7 +11,6 @@ import pytest from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.lineage._helpers.fast_columns import build_fast_project_column_lineage from sqlbuild.compiler.lineage.main._build_native_column_lineage import ( build_native_column_lineage, ) @@ -19,7 +18,7 @@ from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.helpers import mismatches from tests.integration.src.sqlbuild.compiler.lineage._test_types import ( - DeferredLineageTestCase, + FormerlyDeferredLineageTestCase, GeneratedLineageParityTestCase, UnparsedLineageTestCase, ) @@ -51,7 +50,7 @@ ], ids=lambda case: case.description, ) -def test_given_generated_projects_when_building_fast_lineage_natively_then_matches_python( +def test_given_generated_projects_when_building_fast_lineage_then_restored_projects_agree( test_case: GeneratedLineageParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -70,11 +69,16 @@ def test_given_generated_projects_when_building_fast_lineage_natively_then_match for variant in (project, without_compact_facts(project)): for dialect in test_case.dialects: for model_names in (None, selected): - names, python_views, native_views = lineage_views( + names, with_catalog = lineage_views( project=variant, dialect=dialect, model_names=model_names ) + _, restored = lineage_views( + project=replace(variant, binding_catalog=None), + dialect=dialect, + model_names=model_names, + ) differences.extend( - mismatches(inputs=names, expected=python_views, actual=native_views) + mismatches(inputs=names, expected=with_catalog, actual=restored) ) assert differences == [] @@ -102,7 +106,7 @@ def test_given_generated_projects_when_building_fast_lineage_natively_then_match ], ids=lambda case: case.description, ) -def test_given_unparsable_model_when_building_fast_lineage_then_logs_and_omits_like_python( +def test_given_unparsable_model_when_building_fast_lineage_then_logs_and_omits_it( test_case: UnparsedLineageTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -122,44 +126,46 @@ def test_given_unparsable_model_when_building_fast_lineage_then_logs_and_omits_l ) with caplog.at_level(logging.DEBUG, logger="sqlbuild.lineage"): - python: ProjectColumnLineage | None = build_fast_project_column_lineage(project=broken) - python_records: list[tuple[str, str, object]] = lineage_log_records(caplog) - caplog.clear() native: ProjectColumnLineage | None = build_native_column_lineage( project=broken, dialect=None, model_names=None ) native_records: list[tuple[str, str, object]] = lineage_log_records(caplog) - assert native == python assert native is not None assert not native.has_model(project.models[0].name) assert statuses[test_case.expected_status] == 1 - assert native_records == python_records assert tuple(message for _, message, _ in native_records) == test_case.expected_messages @pytest.mark.parametrize( "test_case", [ - DeferredLineageTestCase( - description="a dialect outside the native parser build", + FormerlyDeferredLineageTestCase( + description="a dialect outside the old native parser build", dialect="mysql", keeps_catalog=True, - expected_native_statuses={"deferred": _MODEL_COUNT}, - expected_kind="unsupported_dialect", + expected_native_statuses={"built": _MODEL_COUNT}, + expected_error=None, ), - DeferredLineageTestCase( - description="a project without an analysis catalog to run on", + FormerlyDeferredLineageTestCase( + description="a project restored without its analysis catalog", dialect="duckdb", keeps_catalog=False, - expected_native_statuses={}, - expected_kind="no_analysis_catalog", + expected_native_statuses={"built": _MODEL_COUNT}, + expected_error=None, + ), + FormerlyDeferredLineageTestCase( + description="a dialect name Polyglot does not know raises Python's error", + dialect="nonsense", + keeps_catalog=True, + expected_native_statuses={"unknown_dialect": _MODEL_COUNT}, + expected_error="Unknown dialect: nonsense", ), ], ids=lambda case: case.description, ) -def test_given_deferred_models_when_building_fast_lineage_then_python_builds_and_records( - test_case: DeferredLineageTestCase, +def test_given_formerly_deferred_models_when_building_fast_lineage_then_native_answers( + test_case: FormerlyDeferredLineageTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -176,15 +182,14 @@ def test_given_deferred_models_when_building_fast_lineage_then_python_builds_and compiled, binding_catalog=(None, compiled.binding_catalog)[test_case.keeps_catalog] ) - names, python_views, native_views = lineage_views( - project=project, dialect=test_case.dialect, model_names=None - ) + if test_case.expected_error is None: + _ = lineage_views(project=project, dialect=test_case.dialect, model_names=None) + else: + with pytest.raises(ValueError, match=test_case.expected_error): + _ = lineage_views(project=project, dialect=test_case.dialect, model_names=None) - assert mismatches(inputs=names, expected=python_views, actual=native_views) == [] - assert statuses == Counter(test_case.expected_native_statuses) - assert deferral_records(record_dir) == ( - [{"kind": test_case.expected_kind, "site": "fast_columns.py"}] * _MODEL_COUNT - ) + assert statuses == Counter(test_case.expected_native_statuses), test_case.description + assert deferral_records(record_dir) == [] if __name__ == "__main__": From acc8f36121d53196029207c01c3b9d214cb7edeb Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 17:51:47 +0100 Subject: [PATCH 09/36] feat(native): report model, hook, function and source syntax errors natively Project assembly answers per model whether its SQL parses instead of handing the whole project back, and Python's syntax messages come from one native validate/parse_one entry point. --- .../src/assembly/project/_helpers/syntax.rs | 92 +++++++++++++------ .../src/assembly/project/constants.rs | 5 +- .../main/assemble_project_resources.rs | 26 +++--- .../src/assembly/project/main/mod.rs | 1 + .../assembly/project/main/sql_syntax_error.rs | 17 ++++ .../src/assembly/project/models.rs | 21 +++++ .../project/tests/test_project_assembly.rs | 8 +- .../_helpers/project_assembly/resources.rs | 44 ++++++++- src/sqlbuild/_native.pyi | 7 ++ .../compile/_helpers/analysis/validation.py | 64 +------------ .../compile/_helpers/assembly/project.py | 2 +- .../_helpers/native_stages/assembly.py | 1 + .../project_assembly/_helpers/resources.py | 3 +- .../compiler/project_assembly/models.py | 4 +- .../compiler/project_assembly/types.py | 1 + .../compiler/project_assembly/_test_types.py | 4 +- .../test_native_project_assembly.py | 18 ++-- 17 files changed, 198 insertions(+), 120 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs index 59d20a7422..9dbc62ccf6 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs @@ -1,17 +1,19 @@ -//! Python's `validate_sql_syntax` and `validate_hook_sql_syntax` outcome, without the message. +//! Python's syntax validation: Polyglot's `validate` or `parse_one` of the cleaned SQL. use std::collections::HashMap; use polyglot_sql::{ - ComplexityGuardOptions, Dialect, DialectType, ValidationOptions, validate_with_dialect, + ComplexityGuardOptions, Dialect, DialectType, ParseOptions, ValidationOptions, + validate_with_dialect, }; -use serde_json::json; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_word::is_python_word; use crate::assembly::project::constants::{ - DIALECT_DEFERRAL, MAX_FUNCTION_CALL_DEPTH, NORMALIZATION_DEFERRAL, PLACEHOLDER_DEFERRAL, - PLACEHOLDER_PREFIX, UNSUPPORTED_DIALECT_DEFERRAL, + INVALID_SQL, MAX_FUNCTION_CALL_DEPTH, NORMALIZATION_DEFERRAL, PLACEHOLDER_PREFIX, + UNSUPPORTED_DIALECT_DEFERRAL, }; -use crate::assembly::project::models::SyntaxCheck; +use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure, SyntaxMode}; use crate::assembly::project::types::Fact; use crate::lineage::main::parser_dialect::parser_dialect; use crate::semantic_validation::main::normalize::normalize_analysis_sql; @@ -19,33 +21,72 @@ use crate::semantic_validation::models::NormalizationInput; /// Whether Polyglot's `validate` accepts the cleaned SQL; Python raises where it does not. pub(crate) fn syntax_valid(check: &SyntaxCheck, dialect: &str) -> Fact { + match syntax_error(check, dialect, SyntaxMode::Validate) { + Ok(error) => Ok(error.is_none()), + Err(SyntaxFailure::Normalization(_)) => Err(NORMALIZATION_DEFERRAL.to_owned()), + Err(SyntaxFailure::UnknownDialect(_)) => Err(UNSUPPORTED_DIALECT_DEFERRAL.to_owned()), + } +} + +/// Python's syntax error message for one SQL string, None where Polyglot accepts it. +/// +/// # Errors +/// +/// Where Python raises instead: SQL the analysis normalization rejects, or an unknown dialect. +pub(crate) fn syntax_error( + check: &SyntaxCheck, + dialect: &str, + mode: SyntaxMode, +) -> Result, SyntaxFailure> { let cleaned: String = normalize_analysis_sql(NormalizationInput { sql: check.sql.clone(), dialect: dialect.to_owned(), stubs: HashMap::new(), placeholders: HashMap::new(), }) - .map_err(|_| NORMALIZATION_DEFERRAL.to_owned())?; + .map_err(SyntaxFailure::Normalization)?; let cleaned: String = if check.placeholders.is_empty() { cleaned } else { - placeholder_defaults(&cleaned, &check.placeholders)? + placeholder_defaults(&cleaned, &check.placeholders) }; - let dialect: DialectType = - parser_dialect(Some(dialect)).ok_or_else(|| UNSUPPORTED_DIALECT_DEFERRAL.to_owned())?; - let complexity_guard: ComplexityGuardOptions = - serde_json::from_value(json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH})) - .map_err(|_| DIALECT_DEFERRAL.to_owned())?; - let options = ValidationOptions { - complexity_guard: Some(complexity_guard), - strict_syntax: false, - semantic: false, + let dialect_type: DialectType = parser_dialect(Some(dialect)) + .ok_or_else(|| SyntaxFailure::UnknownDialect(dialect.to_owned()))?; + let complexity_guard = ComplexityGuardOptions { + max_function_call_depth: Some(MAX_FUNCTION_CALL_DEPTH), + ..Default::default() }; - Ok(validate_with_dialect(&cleaned, &Dialect::get(dialect), &options).valid) + let polyglot: Dialect = Dialect::get(dialect_type); + Ok(match mode { + SyntaxMode::Validate => { + let options = ValidationOptions { + complexity_guard: Some(complexity_guard), + strict_syntax: false, + semantic: false, + }; + let result = validate_with_dialect(&cleaned, &polyglot, &options); + (!result.valid).then(|| { + result + .errors + .first() + .map_or_else(|| INVALID_SQL.to_owned(), |error| error.message.clone()) + }) + } + SyntaxMode::Parse => { + let options = ParseOptions { + complexity_guard: Some(complexity_guard), + }; + match polyglot.parse_with_options(&cleaned, &options) { + Ok(statements) if statements.len() == 1 => None, + Ok(statements) => Some(format!("Expected 1 statement, found {}", statements.len())), + Err(error) => Some(error.to_string()), + } + } + }) } /// `substitute_placeholder_defaults`: `@@@name` becomes its default; unknown names stay. -fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> Fact { +fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> String { let mut result: String = String::with_capacity(sql.len()); let mut rest: &str = sql; while let Some(start) = rest.find(PLACEHOLDER_PREFIX) { @@ -54,13 +95,6 @@ fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> Fact Fact bool { - character.is_ascii_alphanumeric() || character == '_' + is_python_word(active_python_text(), character) } diff --git a/crates/sqlbuild-analysis/src/assembly/project/constants.rs b/crates/sqlbuild-analysis/src/assembly/project/constants.rs index dc6f8f874f..37a401397f 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/constants.rs @@ -34,9 +34,8 @@ pub(crate) const PROJECT_VAR_FALSE: &str = "false"; /// The label template errors would name; assembly defers every template error. pub(crate) const TEMPLATE_LABEL: &str = "project assembly template"; pub(crate) const PRESERVED_NAMESPACE_DEFERRAL: &str = "preserved_namespace"; -pub(crate) const SYNTAX_DEFERRAL: &str = "syntax_error"; -pub(crate) const PLACEHOLDER_DEFERRAL: &str = "placeholder_word"; pub(crate) const NORMALIZATION_DEFERRAL: &str = "normalization"; -pub(crate) const DIALECT_DEFERRAL: &str = "dialect"; +/// Python's message when `validate` rejects SQL without naming an error. +pub(crate) const INVALID_SQL: &str = "invalid SQL"; pub(crate) const UNSUPPORTED_DIALECT_DEFERRAL: &str = "unsupported_dialect"; pub(crate) const PANIC_DEFERRAL: &str = "panic"; diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs index add612c439..ce6c488027 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs @@ -9,7 +9,7 @@ use crate::assembly::project::_helpers::deps::{audit_deps, reference_deps}; use crate::assembly::project::_helpers::syntax::syntax_valid; use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; use crate::assembly::project::_helpers::templates::TemplateInputs; -use crate::assembly::project::constants::{PANIC_DEFERRAL, SYNTAX_DEFERRAL}; +use crate::assembly::project::constants::PANIC_DEFERRAL; use crate::assembly::project::models::{ModelFacts, ProjectRequest, ProjectResources}; use crate::assembly::project::types::Fact; @@ -25,9 +25,11 @@ pub fn assemble_project_resources(request: &ProjectRequest) -> Result Fact { - for model in &request.models { - valid_syntax(model, &request.dialect)?; - } + let model_syntax_valid: Vec = request + .models + .iter() + .map(|model| valid_syntax(model, &request.dialect)) + .collect(); let target = request.target.as_ref(); let inputs = TemplateInputs { variables: &request.variables, @@ -45,6 +47,7 @@ fn resources(request: &ProjectRequest) -> Fact { .map(|seed| seed_target(seed, &request.defaults, target, &inputs)) .collect::>()?; Ok(ProjectResources { + model_syntax_valid, model_deps: request .models .iter() @@ -62,12 +65,11 @@ fn resources(request: &ProjectRequest) -> Fact { }) } -/// Python's model and hook syntax validation, which raises for the first SQL it rejects. -fn valid_syntax(model: &ModelFacts, dialect: &str) -> Fact<()> { - for check in &model.syntax_checks { - if !syntax_valid(check, dialect)? { - return Err(SYNTAX_DEFERRAL.to_owned()); - } - } - Ok(()) +/// Whether Python's model and hook syntax validation passes; where it raises, Python's +/// validation runs for that model and reports the failure. +fn valid_syntax(model: &ModelFacts, dialect: &str) -> bool { + model + .syntax_checks + .iter() + .all(|check| syntax_valid(check, dialect) == Ok(true)) } diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs index d35ca0b269..dccf961e02 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs @@ -1,2 +1,3 @@ pub mod assemble_project_resources; pub mod check_sql_syntax; +pub mod sql_syntax_error; diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs b/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs new file mode 100644 index 0000000000..34e479db15 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs @@ -0,0 +1,17 @@ +//! Python's syntax validation message for one SQL string. + +use crate::assembly::project::_helpers::syntax::syntax_error; +use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure, SyntaxMode}; + +/// The message Python's validation raises with, None where Polyglot accepts the SQL. +/// +/// # Errors +/// +/// Where Python raises a different error: rejected normalization or an unknown dialect. +pub fn sql_syntax_error( + check: &SyntaxCheck, + dialect: &str, + mode: SyntaxMode, +) -> Result, SyntaxFailure> { + syntax_error(check, dialect, mode) +} diff --git a/crates/sqlbuild-analysis/src/assembly/project/models.rs b/crates/sqlbuild-analysis/src/assembly/project/models.rs index 92d0f34a3c..d253255695 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/models.rs @@ -52,6 +52,24 @@ pub struct SyntaxCheck { pub placeholders: Pairs, } +/// Which Polyglot entry point Python's validation calls. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SyntaxMode { + /// `validate`, for model queries and hooks. + Validate, + /// `parse_one`, for function bodies and source expressions. + Parse, +} + +/// Where Python's syntax validation raises instead of reporting a message. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SyntaxFailure { + /// The analysis normalization's `ValueError` message. + Normalization(String), + /// Polyglot does not know the dialect name. + UnknownDialect(String), +} + /// One model's references and the SQL strings its assembly validates. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ModelFacts { @@ -114,6 +132,9 @@ pub struct Namespace { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ProjectResources { pub model_deps: Vec>, + /// Whether every SQL string a model's assembly validates parses; where one does not, + /// Python's validation reports it with its message and location. + pub model_syntax_valid: Vec, /// A managed source's `(database, schema)`; None where the entry stays as authored. pub sources: Vec, Option)>>, pub seeds: Vec, diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs index 9dfbd56c08..5ddb9767b5 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs @@ -206,10 +206,10 @@ fn given_model_sql_when_validating_syntax_then_matches_python_or_defers() { }, SyntaxTestCase { dialect: "duckdb", - description: "a placeholder followed by text Python's \\w may extend", + description: "a placeholder name Python's \\w extends past ASCII stays authored", sql: "SELECT @@@x\u{e9} AS a", placeholders: &[("x", "1")], - expected_valid: None, + expected_valid: Some(true), }, SyntaxTestCase { dialect: "trino", @@ -310,9 +310,9 @@ fn given_sql_batches_when_checking_syntax_then_stops_at_python_first_rejection_o expected_valid: Some(false), }, SyntaxBatchTestCase { - description: "a statement native defers before any rejection", + description: "a non-ASCII placeholder name before a rejection", sqls: &["SELECT @@@x\u{e9}", "SELECT FROM WHERE ("], - expected_valid: None, + expected_valid: Some(false), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index 48d24695f1..f82adb197d 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -1,17 +1,23 @@ //! Python's resource assembly facts for one project, computed natively for the preview engine. +use std::collections::HashMap; + +use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::types::Pairs; use sqlbuild_analysis::assembly::project::main::assemble_project_resources::assemble_project_resources; use sqlbuild_analysis::assembly::project::main::check_sql_syntax::check_sql_syntax; +use sqlbuild_analysis::assembly::project::main::sql_syntax_error::sql_syntax_error as check_syntax_error; use sqlbuild_analysis::assembly::project::models::{ AuditFacts, InputRead, ModelFacts, Namespace, ProjectRequest, ProjectResources, Reference, - SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, TargetNamespace, Variable, + SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, SyntaxFailure, SyntaxMode, TargetNamespace, + Variable, }; use sqlbuild_analysis::assembly::project::types::ObjectKey; use sqlbuild_model_config::templates::models::Scalar; +use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::types::CompilerDetach; const TEXT_VARIABLE: &str = "text"; @@ -70,6 +76,7 @@ type ResourcesRow = ( Vec>, Vec>, Vec<(&'static str, String)>, + Vec, ); /// The project's resource facts, or None with the reason Python must assemble it. @@ -85,6 +92,39 @@ fn assemble_project_resource_facts( } } +/// Python's syntax error message for one SQL string, None where it parses; raises where Python +/// raises: `ValueError` for SQL the analysis normalization rejects or an unknown dialect. +#[pyfunction] +#[pyo3(signature = (sql, placeholders, dialect, parse_one=false))] +fn sql_syntax_error( + py: Python<'_>, + sql: String, + placeholders: Option>, + dialect: Option, + parse_one: bool, +) -> PyResult> { + let check = SyntaxCheck { + sql, + placeholders: placeholders.unwrap_or_default().into_iter().collect(), + }; + let dialect: String = dialect + .filter(|dialect| !dialect.is_empty()) + .unwrap_or_else(|| "generic".to_owned()); + let mode = if parse_one { + SyntaxMode::Parse + } else { + SyntaxMode::Validate + }; + py.compiler_detach(|| Ok(check_syntax_error(&check, &dialect, mode))) + .map_err(compiler_error)? + .map_err(|failure| match failure { + SyntaxFailure::Normalization(message) => PyValueError::new_err(message), + SyntaxFailure::UnknownDialect(name) => { + PyValueError::new_err(format!("Unknown dialect: {name}")) + } + }) +} + /// Whether every `(sql, placeholders)` parses, or None with the reason Python must check it. #[pyfunction] fn check_native_sql_syntax( @@ -200,6 +240,7 @@ fn resources_row(resources: ProjectResources) -> ResourcesRow { resources.function_deps, resources.audit_deps, resources.reads.into_iter().map(read_row).collect(), + resources.model_syntax_valid, ) } @@ -222,5 +263,6 @@ fn namespace_row(namespace: Namespace) -> NamespaceRow { pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(assemble_project_resource_facts, module)?)?; module.add_function(wrap_pyfunction!(check_native_sql_syntax, module)?)?; + module.add_function(wrap_pyfunction!(sql_syntax_error, module)?)?; Ok(()) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index e4d6000426..94da5fc929 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -664,6 +664,12 @@ def assemble_compiled_sql_tests( ) -> list[NativeSqlTestAssemblyRow]: ... # Native analysis: compiled project assembly. +def sql_syntax_error( + sql: str, + placeholders: Mapping[str, str] | None, + dialect: str | None, + parse_one: bool = False, +) -> str | None: ... def check_native_sql_syntax( request: tuple[str, list[tuple[str, list[tuple[str, str]]]]], / ) -> tuple[bool | None, str | None]: ... @@ -689,6 +695,7 @@ def assemble_project_resource_facts( list[list[tuple[str, str]]], list[list[tuple[str, str]]], list[tuple[str, str]], + list[bool], ] | None, str | None, diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/validation.py b/src/sqlbuild/compiler/compile/_helpers/analysis/validation.py index 8ef57f338b..80b6228992 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/validation.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/validation.py @@ -3,12 +3,8 @@ from __future__ import annotations from pathlib import Path -from typing import Any -from sqlbuild.compiler.compile._helpers.analysis.columns import ( - _replace_refs_with_stubs, - substitute_placeholder_defaults, -) +import sqlbuild._native as _native from sqlbuild.compiler.compile.constants import SQL_ANALYSIS_OPT_OUT_ENTRY from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.discovery.constants import ( @@ -17,7 +13,6 @@ SQL_ANALYSIS_CONFIG_KEY, ) from sqlbuild.compiler.discovery.models import PythonHookEntry, SqlHookEntry -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql from sqlbuild.errors.setting_help.main.join_helps import join_helps from sqlbuild.errors.setting_help.main.model_header_help import model_header_help from sqlbuild.errors.setting_help.main.setting_help import setting_help @@ -33,13 +28,7 @@ def validate_sql_syntax( ) -> None: """Validate that the model query SQL is parseable by Polyglot.""" - cleaned_sql: str = _replace_refs_with_stubs(query_sql=query_sql, dialect=dialect) - if placeholders: - cleaned_sql = substitute_placeholder_defaults( - query_sql=cleaned_sql, placeholders=placeholders - ) - - error_message: str | None = _validate_sql_with_polyglot(sql=cleaned_sql, dialect=dialect) + error_message: str | None = _native.sql_syntax_error(query_sql, placeholders, dialect) if error_message is None: return raise CompileInputError( @@ -48,21 +37,6 @@ def validate_sql_syntax( ) from None -def _validate_sql_with_polyglot(*, sql: str, dialect: str | None) -> str | None: - polyglot_module: Any = import_polyglot_sql() - try: - result: Any = polyglot_module.validate(sql, dialect=dialect or "generic") - except polyglot_module.PolyglotError as error: - return str(error) - if result: - return None - error_message: str = "invalid SQL" - errors: object = getattr(result, "errors", ()) - if isinstance(errors, list) and errors: - error_message = str(getattr(errors[0], "message", error_message)) - return error_message - - def validate_function_sql_syntax( *, body_sql: str, @@ -187,17 +161,9 @@ def _validate_sql_syntax_with_message( ) -> None: """Parse one SQL expression with Polyglot and raise a contextual error.""" - cleaned_sql: str = _clean_sql_for_validation( - query_sql=query_sql, - placeholders=placeholders, - dialect=dialect, + error_message: str | None = _native.sql_syntax_error( + query_sql, placeholders, dialect, parse_one=True ) - polyglot_module: Any = import_polyglot_sql() - try: - polyglot_module.parse_one(cleaned_sql, dialect=dialect or "generic") - error_message: str | None = None - except polyglot_module.PolyglotError as error: - error_message = str(error) if error_message is not None: _raise_sql_validation_error( error_prefix=error_prefix, error_message=error_message, model_owned=False @@ -213,33 +179,13 @@ def _validate_hook_sql_with_message( ) -> None: """Validate a complete hook execution payload with Polyglot.""" - cleaned_sql: str = _clean_sql_for_validation( - query_sql=query_sql, - placeholders=placeholders, - dialect=dialect, - ) - error_message: str | None = _validate_sql_with_polyglot(sql=cleaned_sql, dialect=dialect) + error_message: str | None = _native.sql_syntax_error(query_sql, placeholders, dialect) if error_message is not None: _raise_sql_validation_error( error_prefix=error_prefix, error_message=error_message, model_owned=True ) -def _clean_sql_for_validation( - *, - query_sql: str, - placeholders: dict[str, str] | None, - dialect: str | None, -) -> str: - cleaned_sql: str = _replace_refs_with_stubs(query_sql=query_sql, dialect=dialect) - if placeholders: - return substitute_placeholder_defaults( - query_sql=cleaned_sql, - placeholders=placeholders, - ) - return cleaned_sql - - def _raise_sql_validation_error( *, error_prefix: str, error_message: str, model_owned: bool ) -> None: diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 8d13e10215..f0e9290f73 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -549,7 +549,7 @@ def _assemble_compiled_model( ) -> CompiledModel: model_name: str = model_input.model_file.file_path.stem native_deps: tuple[CompiledObjectKey, ...] | None = native_model.deps if native_model else None - syntax_validated: bool = native_deps is not None + syntax_validated: bool = native_model is not None and native_model.syntax_valid profile: ExpressionInferenceProfile = inference_profile or ExpressionInferenceProfile() analysis_query_sql: str = cursor_intrinsics_analysis_sql( sql=model_input.query_sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index ecbe869bd2..60a89be3cb 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -98,6 +98,7 @@ def project_facts_by_engine( NativeModelFacts( deps=resources.model_deps[index] if resources is not None else None, dynamic_contract=proofs.get(index), + syntax_valid=resources is not None and resources.model_syntax_valid[index], ) for index in range(len(inputs.model_inputs)) ), diff --git a/src/sqlbuild/compiler/project_assembly/_helpers/resources.py b/src/sqlbuild/compiler/project_assembly/_helpers/resources.py index 4475507e1e..d5de61d0d6 100644 --- a/src/sqlbuild/compiler/project_assembly/_helpers/resources.py +++ b/src/sqlbuild/compiler/project_assembly/_helpers/resources.py @@ -28,10 +28,11 @@ def project_resources( ) -> NativeProjectResources: """Return the resource facts the native row describes, in input order.""" - model_deps, source_namespaces, seed_namespaces, function_deps, audit_deps, reads = row + model_deps, source_namespaces, seed_namespaces, function_deps, audit_deps, reads, syntax = row _ = [_replay_read(kind=kind, name=name) for kind, name in reads] return NativeProjectResources( model_deps=tuple(_keys(keys) for keys in model_deps), + model_syntax_valid=tuple(syntax), source_entries=tuple( _source_entry(source_input=source_input, namespace=namespace) for source_input, namespace in zip(inputs.source_inputs, source_namespaces, strict=True) diff --git a/src/sqlbuild/compiler/project_assembly/models.py b/src/sqlbuild/compiler/project_assembly/models.py index 7905511275..4d12b8182e 100644 --- a/src/sqlbuild/compiler/project_assembly/models.py +++ b/src/sqlbuild/compiler/project_assembly/models.py @@ -14,9 +14,10 @@ @dataclass(frozen=True, slots=True) class NativeProjectResources: - """Per-resource facts in input order; every requested SQL string passed syntax validation.""" + """Per-resource facts in input order, and whether each model's SQL passed syntax validation.""" model_deps: tuple[tuple[CompiledObjectKey, ...], ...] + model_syntax_valid: tuple[bool, ...] source_entries: tuple[SourceEntry, ...] seed_destinations: tuple[CompiledRelationLocation, ...] function_deps: tuple[tuple[CompiledObjectKey, ...], ...] @@ -29,6 +30,7 @@ class NativeModelFacts: deps: tuple[CompiledObjectKey, ...] | None dynamic_contract: DynamicColumnContractProof | None + syntax_valid: bool = False @dataclass(frozen=True, slots=True) diff --git a/src/sqlbuild/compiler/project_assembly/types.py b/src/sqlbuild/compiler/project_assembly/types.py index a3e8ccf62b..4583e8bae2 100644 --- a/src/sqlbuild/compiler/project_assembly/types.py +++ b/src/sqlbuild/compiler/project_assembly/types.py @@ -32,4 +32,5 @@ list[list[ObjectKeyRow]], list[list[ObjectKeyRow]], list[tuple[str, str]], + list[bool], ] diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py index 1e010ed519..09878fbcea 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py @@ -18,11 +18,11 @@ class GeneratedAssemblyParityTestCase: @dataclass(frozen=True) class AssemblyDeferralTestCase: - """A project native hands back to Python, recording why, with Python's exact outcome.""" + """A project Python rejects: native raises the same error, deferring only where recorded.""" description: str files: dict[str, str] - expected_kind: str + expected_kind: str | None expected_error: str diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index 514932ed6b..91357b48e5 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -124,7 +124,7 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( "sqlbuild_project.toml": _PROJECT_TOML, "models/orders.sql": 'MODEL (description "Orders");\n\nSELECT FROM WHERE (\n', }, - expected_kind="syntax_error", + expected_kind=None, expected_error="SQL syntax error in model 'orders'", ), AssemblyDeferralTestCase( @@ -136,7 +136,7 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( ");\n\nSELECT 1 AS n\n" ), }, - expected_kind="syntax_error", + expected_kind=None, expected_error="Polyglot could not parse model 'orders' pre_hooks[0]", ), AssemblyDeferralTestCase( @@ -171,7 +171,7 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( ], ids=lambda case: case.description, ) -def test_given_projects_python_rejects_when_assembling_natively_then_defers_with_python_error( +def test_given_projects_python_rejects_when_assembling_natively_then_raises_python_error( test_case: AssemblyDeferralTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -194,8 +194,10 @@ def test_given_projects_python_rejects_when_assembling_natively_then_defers_with assert test_case.expected_error in str(python_error.value) assert str(native_error.value) == str(python_error.value) - assert assembly_deferrals(record_dir) == Counter( - {f"project_assembly:{test_case.expected_kind}": 1} + assert assembly_deferrals(record_dir) == ( + Counter({f"project_assembly:{test_case.expected_kind}": 1}) + if test_case.expected_kind is not None + else Counter() ) @@ -241,8 +243,10 @@ def test_given_a_variable_only_python_renders_when_assembling_natively_then_pyth assert assembly_view(native) == assembly_view(python) assert native.seeds[0].destination.schema == test_case.expected_schema - assert assembly_deferrals(record_dir) == Counter( - {f"project_assembly:{test_case.expected_kind}": 1} + assert assembly_deferrals(record_dir) == ( + Counter({f"project_assembly:{test_case.expected_kind}": 1}) + if test_case.expected_kind is not None + else Counter() ) From 1634a4a5c3975153b760602c80d877f9e8d04cb6 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:34:14 +0100 Subject: [PATCH 10/36] feat(native): fail compilation on internal native analysis failures Native model analysis no longer hands models, enrichments, dynamic pivot proofs or whole sessions back to Python. An internal native failure (a panic or a broken invariant) raises NativeCompilerError with its context and fails the compile; adapter rule exceptions and the ValueError Python's analysis raised for a dialect alias keep their original types. Non-ASCII function names now resolve declared return types natively. Deletes the Python whole-project analysis driver, binding waves and dataflow, the SQLite analysis cache, the Python dynamic pivot proof and their fallback-only tests; parity tests now compare uncached, cached and re-proven native runs. --- .../_helpers/analysis_cache.rs | 6 +- .../_helpers/dynamic_pivot.rs | 55 +- .../analysis_session/_helpers/enrichment.rs | 111 +- .../analysis_session/_helpers/session.rs | 280 ++-- .../assembly/analysis_session/constants.rs | 5 +- .../src/assembly/analysis_session/main/mod.rs | 1 - .../main/prove_dynamic_contracts.rs | 6 +- .../main/prove_finished_dynamic_contracts.rs | 4 +- .../main/provide_deferred_analyses.rs | 11 - .../main/run_analysis_session.rs | 4 +- .../main/start_analysis_session.rs | 8 +- .../src/assembly/analysis_session/models.rs | 57 +- .../analysis_session/tests/helpers.rs | 65 +- .../tests/test_analysis_cache.rs | 2 +- .../tests/test_analysis_session.rs | 64 +- .../analysis_session/tests/test_types.rs | 9 +- crates/sqlbuild-core/src/constants.rs | 4 + .../src/panics/main/is_compiler_panic.rs | 5 +- crates/sqlbuild-core/src/panics/main/mod.rs | 1 + .../src/panics/main/native_failure.rs | 11 + .../src/panics/tests/test_panics.rs | 11 + .../_helpers/analysis_session/session.rs | 216 +-- .../_helpers/dynamic_contracts.py | 48 +- .../_helpers/model_analysis.py | 49 +- .../analysis_session/_helpers/session_rows.py | 43 +- .../classes/native_model_analysis.py | 94 +- .../compiler/analysis_session/constants.py | 9 - .../main/_analyze_native_model_sql.py | 4 +- .../compiler/analysis_session/types.py | 6 +- .../compile/_helpers/analysis/cache.py | 1038 +-------------- .../_helpers/analysis/dynamic_pivot.py | 728 ---------- .../_helpers/assembly/binding_waves.py | 109 -- .../compile/_helpers/assembly/project.py | 854 +----------- .../_helpers/native_stages/assembly.py | 34 +- .../compile/classes/binding_dataflow.py | 314 ----- .../compile/classes/python_model_analysis.py | 94 +- src/sqlbuild/compiler/compile/models.py | 28 - tests/conftest.py | 6 +- .../compiler_differential/_test_types.py | 13 - .../scripts/compiler_differential/helpers.py | 25 - .../test_compiler_differential.py | 66 - .../cli/commands/main/compile/_test_types.py | 7 - .../cli/commands/main/compile/helpers.py | 8 - .../compile/test_compile_incremental_edits.py | 4 - .../compile/test_dense_batched_preparation.py | 61 - .../compiler/analysis_session/_test_types.py | 16 +- .../compiler/analysis_session/helpers.py | 306 ++--- .../test_native_model_analysis.py | 192 ++- .../sqlbuild/compiler/pipeline/_test_types.py | 124 -- .../src/sqlbuild/compiler/pipeline/helpers.py | 544 +------- .../test_batched_analysis_preparation.py | 201 --- .../pipeline/test_dataflow_analysis_oracle.py | 215 --- .../test_dataflow_analysis_schedule.py | 330 ----- .../pipeline/test_expression_catalog_cache.py | 2 +- .../pipeline/test_shared_binding_queries.py | 351 ----- .../compiler/compile/_helpers/_test_types.py | 24 - .../_helpers/analysis/test_dynamic_pivot.py | 79 +- .../compiler/compile/_helpers/helpers.py | 34 - .../_helpers/native_stages/_test_types.py | 16 - .../test_dynamic_column_contract_by_engine.py | 114 -- .../compile/_helpers/test_analysis_cache.py | 1174 ----------------- 61 files changed, 726 insertions(+), 7574 deletions(-) delete mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/provide_deferred_analyses.rs create mode 100644 crates/sqlbuild-core/src/panics/main/native_failure.rs delete mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/dynamic_pivot.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/assembly/binding_waves.py delete mode 100644 src/sqlbuild/compiler/compile/classes/binding_dataflow.py delete mode 100644 tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_batched_preparation.py delete mode 100644 tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py delete mode 100644 tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_oracle.py delete mode 100644 tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py delete mode 100644 tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py delete mode 100644 tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py delete mode 100644 tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py delete mode 100644 tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs index f725df3e1e..21cc9eb6c3 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs @@ -33,7 +33,6 @@ impl AnalysisSession { session_digest: session_digest(&self.request), keys: vec![None; models], hits: vec![false; models], - uncacheable: vec![false; models], legacy_tables: vec![None; models], stats: AnalysisCacheStats::default(), }); @@ -412,12 +411,11 @@ impl<'a> Decoder<'a> { pub(crate) fn encode_outcome( outcome: &ModelOutcome, legacy_tables: Option<&ContentDigest>, -) -> Option> { +) -> Vec { let analysis: &ModelAnalysis = &outcome.analysis; let (tag, rows) = match &analysis.lineage { LineageFacts::Native(rows) => (LINEAGE_NATIVE, rows), LineageFacts::NativeFacts(rows) => (LINEAGE_FACTS, rows), - LineageFacts::PythonAnalysis | LineageFacts::PythonEnrichment => return None, }; let mut encoder: Encoder<'_> = Encoder::default(); match legacy_tables { @@ -465,7 +463,7 @@ pub(crate) fn encode_outcome( encoder.text(severity); } encoder.flag(analysis.binding_validated); - Some(encoder.into_bytes()) + encoder.into_bytes() } /// The outcome [`encode_outcome`] stored, validated against `schema`, without cleaned SQL. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index cd9b7a5f82..eceaf3610d 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -1,12 +1,12 @@ //! Python's `analyze_dynamic_column_contract` over the crate's parse of the model SQL. use std::collections::{HashMap, HashSet}; -use std::panic::{AssertUnwindSafe, catch_unwind}; use rayon::ThreadPool; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use serde_json::{Map, Value}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; use crate::assembly::analysis_session::_helpers::dict_walk::{ casefold, column_name, dict_list, identifier_name, is_single_wildcard, nested, node_key, @@ -23,6 +23,9 @@ use crate::assembly::analysis_session::models::{ }; use crate::assembly::analysis_session::types::Shapes; +/// What an internal native failure of a pivot proof names. +const PIVOT_CONTEXT: &str = "native dynamic pivot proof"; + /// Stands in for Python's `{}`: neither has a node key. static MISSING_EXPRESSION: Value = Value::Null; @@ -35,28 +38,32 @@ pub(crate) struct PivotFacts<'a> { pub(crate) families_by_table: &'a [(String, Vec)], } -/// Each model's proof in order; a model whose proof panics is deferred to Python. -pub(crate) fn pivot_outcomes(models: &[PivotModel], tables: &PivotTables) -> Vec { +/// Each model's proof in order; the first model whose proof fails internally fails them all. +pub(crate) fn pivot_outcomes( + models: &[PivotModel], + tables: &PivotTables, +) -> Result, String> { let facts = pivot_facts(tables); models .iter() - .map(|model| guarded_outcome(model, &facts)) + .map(|model| pivot_outcome(&model.sql, &model.families, &facts)) .collect() } -/// Each model's proof in order on `pool`; a model whose proof panics is deferred to Python. +/// Each model's proof in order on `pool`; the first internal failure in model order fails them all. pub(crate) fn pooled_pivot_outcomes( pool: &ThreadPool, models: &[PivotModel], tables: &PivotTables, -) -> Vec { +) -> Result, String> { let facts = pivot_facts(tables); - pool.install(|| { + let outcomes: Vec> = pool.install(|| { models .par_iter() - .map(|model| guarded_outcome(model, &facts)) + .map(|model| pivot_outcome(&model.sql, &model.families, &facts)) .collect() - }) + }); + outcomes.into_iter().collect() } fn pivot_facts(tables: &PivotTables) -> PivotFacts<'_> { @@ -69,13 +76,6 @@ fn pivot_facts(tables: &PivotTables) -> PivotFacts<'_> { } } -fn guarded_outcome(model: &PivotModel, facts: &PivotFacts<'_>) -> PivotOutcome { - catch_unwind(AssertUnwindSafe(|| { - pivot_outcome(&model.sql, &model.families, facts) - })) - .unwrap_or(PivotOutcome::Deferred) -} - /// One CTE body by folded name; None for a CTE with column aliases. type CteMap<'a> = HashMap>; @@ -85,37 +85,38 @@ enum Boundary<'a> { Passthrough(&'a str), } -/// The proof Python computes, or a deferral where its result is not reproduced exactly. +/// The proof Python computes. +/// +/// # Errors +/// +/// An internal native failure: a panic, or a parsed tree the proof cannot read. pub(crate) fn pivot_outcome( sql: &str, families: &[DynamicFamily], facts: &PivotFacts<'_>, -) -> PivotOutcome { +) -> Result { if families.is_empty() { - return PivotOutcome::Absent; + return Ok(PivotOutcome::Absent); } let folded_dialect: String = casefold(facts.dialect); if !SUPPORTED_PIVOT_DIALECTS.contains(&folded_dialect.as_str()) { - return failure(format!( + return Ok(failure(format!( "adapter dialect '{}' does not support compiler-proven dynamic pivots", facts.dialect - )); + ))); } let parse_dialect: &str = if folded_dialect == MOTHERDUCK_DIALECT { DUCKDB_DIALECT } else { folded_dialect.as_str() }; - let outcome: Result = catch_compiler_panic(|| { + catch_compiler_panic(|| { Ok(match parsed(sql, parse_dialect)? { Ok(root) => proof(&root, &folded_dialect, families, facts), Err(error) => failure(format!("dynamic pivot SQL could not be parsed: {error}")), }) - }); - match outcome { - Ok(outcome) => outcome, - Err(_) => PivotOutcome::Deferred, - } + }) + .map_err(|reason| native_failure(PIVOT_CONTEXT, &reason)) } /// Python's `parse_one(...).to_dict()`, or the parse error Python reports. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index 0015320b61..c96e636714 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -4,20 +4,21 @@ use std::collections::{HashMap, HashSet}; use polyglot_sql::{AnalyzeQueryOptions, analyze_query}; use serde_json::{Map, Value, json}; +use sqlbuild_core::constants::PYTHON_VALUE_ERROR; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::analysis_session::_helpers::cte_facts::{ LegacyAnalysis, LegacyInput, Recovery, RecoveryInput, RecoveryProfile, legacy_analysis, recovery, }; -use crate::assembly::analysis_session::_helpers::dict_walk::truthy; +use crate::assembly::analysis_session::_helpers::dict_walk::{truthy, upper}; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; use crate::assembly::analysis_session::constants::{ CAST_TRANSFORM, CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, FILTER_CONTEXT, - MAX_FUNCTION_CALL_DEPTH, NON_NULL_NULLABILITY, NULL_KEYWORD, RESOLVED_SOURCE_CONFIDENCE, - SELECT_SHAPE, SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, - TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, - WILDCARD, + INVALID_ANALYZE_OPTIONS, MAX_FUNCTION_CALL_DEPTH, NON_NULL_NULLABILITY, NULL_KEYWORD, + RESOLVED_SOURCE_CONFIDENCE, SELECT_SHAPE, SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, + TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, + UNKNOWN_NULLABILITY, UNKNOWN_TYPE, WILDCARD, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow, ModelRequest}; use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; @@ -44,34 +45,32 @@ pub(crate) struct Enrichment { pub(crate) has_star: bool, } -/// Python's compact re-analysis: answered, declined to the legacy analysis, or deferred. +/// Python's compact re-analysis: answered, or declined to the legacy analysis. enum Projected { Native(Enrichment), Declined, - Deferred, } /// One projection's upstream sources and confidence code. type Upstream = (Vec<(String, String, String)>, u8); -/// Python's compact re-analysis, or None where Python would take another path. -pub(crate) fn enrichment(input: &EnrichmentInput<'_>) -> Option { - let enrichment: Result, String> = catch_compiler_panic(|| analysed(input)); - enrichment.unwrap_or_default() +/// Python's compact re-analysis. +/// +/// # Errors +/// +/// An internal native failure, panics included. +pub(crate) fn enrichment(input: &EnrichmentInput<'_>) -> Result { + catch_compiler_panic(|| analysed(input)) } -/// The re-analysis; Ok(None) where Python takes another path, Err for an unexpected failure. -fn analysed(input: &EnrichmentInput<'_>) -> Result, String> { - let normalized: Result = normalize_analysis_sql(NormalizationInput { +fn analysed(input: &EnrichmentInput<'_>) -> Result { + let cleaned: String = normalize_analysis_sql(NormalizationInput { sql: input.model.query_sql.clone(), dialect: input.dialect.to_owned(), stubs: HashMap::new(), placeholders: input.model.placeholders.iter().cloned().collect(), - }); - let cleaned: String = match normalized { - Ok(cleaned) => cleaned, - Err(_) => return Ok(None), - }; + }) + .map_err(|_| "the analysed SQL no longer normalizes".to_owned())?; let mut options: Map = Map::new(); options.insert( "dialect".to_owned(), @@ -84,22 +83,21 @@ fn analysed(input: &EnrichmentInput<'_>) -> Result, String> { "complexityGuard".to_owned(), json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH}), ); - let options: AnalyzeQueryOptions = - serde_json::from_value(Value::Object(options)).map_err(|error| error.to_string())?; + let options: AnalyzeQueryOptions = serde_json::from_value(Value::Object(options)) + .map_err(|error| format!("{PYTHON_VALUE_ERROR}{INVALID_ANALYZE_OPTIONS}{error}"))?; let analysis = match analyze_query(&cleaned, options) { Ok(analysis) => analysis, - Err(_) => return Ok(legacy(input, &cleaned)), + Err(_) => return legacy(input, &cleaned), }; let analysis: Value = serde_json::to_value(analysis).map_err(|error| error.to_string())?; - Ok(match projected(input, &analysis, &cleaned) { - Projected::Native(enrichment) => Some(enrichment), + match projected(input, &analysis, &cleaned)? { + Projected::Native(enrichment) => Ok(enrichment), Projected::Declined => legacy(input, &cleaned), - Projected::Deferred => None, - }) + } } /// Python's legacy analysis once compact analysis declines, over the input shapes. -fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Option { +fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Result { let types: ShapeTable = ShapeTable::from_shapes(input.input_schemas); let unknown: Shapes = input .input_schemas @@ -107,7 +105,7 @@ fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Option { .map(|(name, shape)| (name.clone(), unknown_shape(shape))) .collect(); let nullability: ShapeTable = ShapeTable::from_shapes(&unknown); - let analysis: LegacyAnalysis = match legacy_analysis(&LegacyInput { + let analysis: LegacyAnalysis = legacy_analysis(&LegacyInput { cleaned_sql: cleaned, lineage_references: &input.model.lineage_references, recover: input.model.recover_cte_facts, @@ -119,11 +117,8 @@ fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Option { rules: input.nullability_rules, callback: input.nullability_callback, }, - }) { - Ok(analysis) => analysis, - Err(_deferred) => return None, - }; - Some(Enrichment { + })?; + Ok(Enrichment { analysis_succeeded: analysis.succeeded, columns: analysis.columns, lineage: analysis.lineage, @@ -169,29 +164,30 @@ fn analysis_schema(input: &EnrichmentInput<'_>) -> Option { } /// Python's projection of `analyze_query` facts into columns and lineage. -fn projected(input: &EnrichmentInput<'_>, analysis: &Value, cleaned: &str) -> Projected { +fn projected( + input: &EnrichmentInput<'_>, + analysis: &Value, + cleaned: &str, +) -> Result { let Some(projections) = analysis.get("projections").and_then(Value::as_array) else { - return Projected::Declined; + return Ok(Projected::Declined); }; let shape: Option<&str> = analysis.get("shape").and_then(Value::as_str); if !matches!(shape, Some(SELECT_SHAPE | SET_OPERATION_SHAPE)) || !eligible(projections) { - return Projected::Declined; - } - match native_projection(input, analysis, cleaned, projections) { - Some(enrichment) => Projected::Native(enrichment), - None => Projected::Deferred, + return Ok(Projected::Declined); } + native_projection(input, analysis, cleaned, projections).map(Projected::Native) } -/// The compact facts as Python's columns and lineage, None where native defers. +/// The compact facts as Python's columns and lineage. fn native_projection( input: &EnrichmentInput<'_>, analysis: &Value, cleaned: &str, projections: &[Value], -) -> Option { +) -> Result { let shape: Option<&str> = analysis.get("shape").and_then(Value::as_str); - let recovery: Recovery = match recovery(&RecoveryInput { + let recovery: Recovery = recovery(&RecoveryInput { cleaned_sql: cleaned, input_schemas: input.input_schemas, recover: recovers_cte_facts(input, analysis), @@ -202,10 +198,7 @@ fn native_projection( rules: input.nullability_rules, callback: input.nullability_callback, }, - }) { - Ok(recovery) => recovery, - Err(_deferred) => return None, - }; + })?; let resources: HashMap<&str, (&str, &str)> = input .model .lineage_references @@ -216,7 +209,9 @@ fn native_projection( let mut columns: Vec = Vec::new(); let mut lineage: Vec = Vec::new(); for projection in projections { - let projection: &Map = projection.as_object()?; + let projection: &Map = projection + .as_object() + .ok_or("an eligible projection that is not an object")?; if truthy(projection.get("isStar")) { continue; } @@ -228,7 +223,7 @@ fn native_projection( let data_type: Option = if direct { recovered(&recovery.types, name).cloned() } else { - projection_type(projection, input.function_return_types)? + projection_type(projection, input.function_return_types) }; let nullability: &str = if recovery.non_null_outputs.contains(name) { NON_NULL_NULLABILITY @@ -271,7 +266,7 @@ fn native_projection( .unwrap_or(fallback) }); } - Some(Enrichment { + Ok(Enrichment { analysis_succeeded: true, columns: Some(columns), lineage, @@ -329,10 +324,10 @@ fn has_null_filter(analysis: &Value) -> bool { fn projection_type( projection: &Map, function_return_types: &Pairs, -) -> Option> { +) -> Option { let cast_type: &str = text(projection.get("castType")); if !cast_type.is_empty() && cast_type != UNKNOWN_TYPE { - return Some(Some(cast_type.to_owned())); + return Some(cast_type.to_owned()); } let function_name: Option<&str> = projection .get("transformFunction") @@ -340,22 +335,16 @@ fn projection_type( .and_then(|function| function.get("name")) .and_then(Value::as_str); if let Some(function_name) = function_name { - if !function_name.is_ascii() { - return None; - } - let upper: String = function_name.to_ascii_uppercase(); + let upper: String = upper(function_name); if let Some((_, declared)) = function_return_types .iter() .find(|(name, _)| *name == upper) { - return Some(Some(declared.clone())); + return Some(declared.clone()); } } let type_hint: &str = text(projection.get("typeHint")); - if !type_hint.is_empty() && type_hint != UNKNOWN_TYPE { - return Some(Some(type_hint.to_owned())); - } - Some(None) + (!type_hint.is_empty() && type_hint != UNKNOWN_TYPE).then(|| type_hint.to_owned()) } fn projection_nullability(projection: &Map, infer: bool) -> &'static str { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index f274248dcf..e95449cd89 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -7,7 +7,9 @@ use rayon::ThreadPool; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; +use sqlbuild_core::constants::PYTHON_VALUE_ERROR; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; use crate::assembly::analysis_session::_helpers::analysis_cache::{ CachedOutcome, decode_outcome, encode_outcome, @@ -28,25 +30,30 @@ use crate::assembly::analysis_session::_helpers::mappings::{ }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; use crate::assembly::analysis_session::_helpers::publication::{ShapeOptions, ShapeSource}; -use crate::assembly::analysis_session::constants::{ - DEFERRAL_ANALYSIS, DEFERRAL_ENRICHMENT, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, -}; +use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::models::{ - AnalysisSession, Awaiting, ColumnFact, Deferral, DeferredAnalysis, FinishedSession, - LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, - PivotTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, + AnalysisSession, ColumnFact, FinishedSession, LineageFacts, LineageRow, ModelAnalysis, + ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, SessionModelFacts, + SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::ProjectCatalog; use crate::semantic_validation::types::DiagnosticRow; -/// The enrichment deferrals of one wave and whether each model's star was pending. -type Enrichments = (Vec, HashMap); +/// What an internal native failure of the session names. +const SESSION_CONTEXT: &str = "native model analysis"; impl AnalysisSession { - /// A session over `request`, or None where two models share a name; a `ref` cycle is one wave. - pub(crate) fn start(request: SessionRequest, catalog: &ProjectCatalog) -> Option { - let scheduled: Option>> = waves(&producers(&request.models)?); + /// A session over `request`; a `ref` cycle is one wave. + /// + /// # Errors + /// + /// A native failure where two models share a name: discovery rejects duplicate model names + /// (D007) before any analysis, so this is a broken invariant. + pub(crate) fn start(request: SessionRequest, catalog: &ProjectCatalog) -> Result { + let producers: Vec> = producers(&request.models) + .ok_or_else(|| native_failure(SESSION_CONTEXT, "two analysed models share a name"))?; + let scheduled: Option>> = waves(&producers); let complete_shapes: ShapeTable = ShapeTable::from_shapes(&request.complete_schemas); let dependency_ordered: bool = scheduled.is_some() && request @@ -56,7 +63,7 @@ impl AnalysisSession { .any(|name| complete_shapes.get(name).is_none_or(Vec::is_empty)); let cyclic: bool = scheduled.is_none(); let waves: Vec> = scheduled.unwrap_or_default(); - Some(Self { + Ok(Self { catalog: SessionCatalog::new(catalog, &request.catalog_schemas), available_types: ShapeTable::from_shapes(&request.column_types), available_nullability: ShapeTable::from_shapes(&request.column_nullability), @@ -78,41 +85,23 @@ impl AnalysisSession { }) } - /// Advance until Python must answer deferrals; a step without deferrals means done. + /// Analyse every wave; any internal native failure fails the whole analysis. pub(crate) fn run(&mut self) -> Result { loop { match std::mem::replace(&mut self.phase, Phase::Done) { - Phase::Await(awaiting) => { - self.phase = Phase::Await(awaiting); - return Err("the session is waiting for deferred analyses".to_owned()); - } - Phase::Done => return Ok(self.step(Vec::new())), + Phase::Done => return Ok(self.step()), Phase::Analyze => { let Some(models) = self.waves.get(self.next_wave).cloned() else { - return Ok(self.step(Vec::new())); + return Ok(self.step()); }; let wave: usize = self.next_wave; self.next_wave += 1; - let deferrals: Vec = self.analyze_wave(&models)?; - if deferrals.is_empty() { - self.phase = Phase::Complete(wave); - continue; - } - let deferred: Vec = deferrals.iter().map(Deferral::model).collect(); - self.phase = Phase::Await(Awaiting::Analyses { - wave, - models: deferred, - }); - return Ok(self.step(deferrals)); + self.analyze_wave(&models)?; + self.phase = Phase::Complete(wave); } Phase::Complete(wave) => { - let (deferrals, star_pending) = self.complete_wave(wave)?; - if deferrals.is_empty() { - self.phase = Phase::Finish(wave); - continue; - } - self.phase = Phase::Await(Awaiting::Enrichments { wave, star_pending }); - return Ok(self.step(deferrals)); + self.complete_wave(wave)?; + self.phase = Phase::Finish(wave); } Phase::Finish(wave) => { self.finish_wave(wave)?; @@ -122,50 +111,6 @@ impl AnalysisSession { } } - /// Answer the deferrals of the last step. - pub(crate) fn provide(&mut self, results: Vec) -> Result<(), String> { - let Phase::Await(awaiting) = std::mem::replace(&mut self.phase, Phase::Done) else { - return Err("the session is not waiting for deferred analyses".to_owned()); - }; - match awaiting { - Awaiting::Analyses { wave, models } => { - let mut answered: Vec = results.iter().map(|result| result.model).collect(); - answered.sort_unstable(); - let mut expected: Vec = models; - expected.sort_unstable(); - if answered != expected { - return Err("deferred analyses do not match the deferrals".to_owned()); - } - for result in results { - let model: usize = result.model; - self.mark_uncacheable(model); - self.outcome_mut(model)?.analysis = python_analysis(result); - } - self.phase = Phase::Complete(wave); - } - Awaiting::Enrichments { wave, star_pending } => { - if results.len() != star_pending.len() { - return Err("deferred enrichments do not match the deferrals".to_owned()); - } - for result in results { - let pending: bool = *star_pending - .get(&result.model) - .ok_or("an enrichment answers no deferral")?; - self.mark_uncacheable(result.model); - let outcome: &mut ModelOutcome = self.outcome_mut(result.model)?; - outcome.analysis = enriched_analysis( - &outcome.analysis, - result, - pending, - LineageFacts::PythonEnrichment, - ); - } - self.phase = Phase::Finish(wave); - } - } - Ok(()) - } - /// Every model's outcome and the binding catalog changes Python records. pub(crate) fn finish(self) -> Result<(SessionOutcome, FinishedSession), String> { if !matches!(self.phase, Phase::Done) { @@ -220,18 +165,18 @@ impl AnalysisSession { families_by_table: &self.request.dynamic_families_by_table, }; let pool = self.catalog.native.analysis_pool()?; - Ok(pool.install(|| { + let outcomes: Vec> = pool.install(|| { models .par_iter() .map(|model| pivot_outcome(&model.pivot_sql, &model.dynamic_families, &facts)) .collect() - })) + }); + outcomes.into_iter().collect() } - fn step(&mut self, deferrals: Vec) -> SessionStep { + fn step(&mut self) -> SessionStep { SessionStep { publications: std::mem::take(&mut self.publications), - deferrals, failures: std::mem::take(&mut self.failures), } } @@ -240,11 +185,11 @@ impl AnalysisSession { self.outcomes .get_mut(model) .and_then(Option::as_mut) - .ok_or_else(|| "a deferral names an unanalysed model".to_owned()) + .ok_or_else(|| native_failure(SESSION_CONTEXT, "a model was left unanalysed")) } /// Python's `_analyze_model_sql_requests` for one wave, less the models the cache answers. - fn analyze_wave(&mut self, models: &[usize]) -> Result, String> { + fn analyze_wave(&mut self, models: &[usize]) -> Result<(), String> { let schemas: Vec = models .iter() .map(|model| self.binding_schema(&self.request.models[*model])) @@ -264,7 +209,6 @@ impl AnalysisSession { nullability: &self.available_nullability, }; let results: Vec = self.catalog.analyze_batch(&batch)?; - let mut deferrals: Vec = Vec::new(); let mut tables: Option = None; for (position, result) in misses.into_iter().zip(results) { let model: &usize = &models[position]; @@ -292,22 +236,12 @@ impl AnalysisSession { failed_analysis(LineageFacts::Native(Vec::new()), diagnostics) } MemberAnalysis::Legacy { lineage } => { - match self.native_legacy(&self.request.models[*model], &result.cleaned_sql) { - Ok(legacy) => { - self.record_legacy_tables(*model, &mut tables); - legacy_outcome(legacy, lineage, diagnostics) - } - Err(_deferred) => { - deferrals.push(Deferral::Analysis { - model: *model, - cleaned_sql: result.cleaned_sql.clone(), - binding_schema: schema.clone(), - binding_diagnostics: diagnostics.clone(), - lineage, - }); - failed_analysis(LineageFacts::PythonAnalysis, diagnostics) - } - } + let request: &ModelRequest = &self.request.models[*model]; + let legacy: LegacyAnalysis = + self.native_legacy(request, &result.cleaned_sql) + .map_err(|reason| native_failure(&model_context(request), &reason))?; + self.record_legacy_tables(*model, &mut tables); + legacy_outcome(legacy, lineage, diagnostics) } }; self.outcomes[*model] = Some(ModelOutcome { @@ -317,7 +251,7 @@ impl AnalysisSession { fused_binding_validated: true, }); } - Ok(deferrals) + Ok(()) } /// Python's legacy analysis of a model the engine handed back, or why Python must answer. @@ -358,12 +292,10 @@ impl AnalysisSession { } /// Python's `_complete_inferred_bindings` star and type pass for one wave. - fn complete_wave(&mut self, wave: usize) -> Result { - let mut deferrals: Vec = Vec::new(); + fn complete_wave(&mut self, wave: usize) -> Result<(), String> { if self.cyclic { - return Ok((deferrals, HashMap::new())); + return Ok(()); } - let mut star_pending_by_model: HashMap = HashMap::new(); let mut candidates: Vec<(usize, Shapes, bool)> = Vec::new(); for model in self.waves[wave].clone() { if self.cached(model) { @@ -395,39 +327,20 @@ impl AnalysisSession { candidates.push((model, input_schemas, star_pending)); } } - let enrichments: Vec> = self.native_enrichments(&candidates)?; - for ((model, input_schemas, star_pending), native) in - candidates.into_iter().zip(enrichments) - { - let Some(native) = native else { - star_pending_by_model.insert(model, star_pending); - deferrals.push(Deferral::Enrichment { - model, - input_schemas, - }); - continue; - }; + let enrichments: Vec = self.native_enrichments(&candidates)?; + for ((model, _, star_pending), native) in candidates.into_iter().zip(enrichments) { let outcome: &mut ModelOutcome = self.outcome_mut(model)?; - outcome.analysis = enriched_analysis( - &outcome.analysis, - native_answer( - model, - native.analysis_succeeded, - native.columns, - native.has_star, - ), - star_pending, - LineageFacts::NativeFacts(native.lineage), - ); + outcome.analysis = enriched_analysis(&outcome.analysis, native, star_pending); } - Ok((deferrals, star_pending_by_model)) + Ok(()) } - /// Python's re-analysis of each candidate natively, None where Python must answer it. + /// Python's re-analysis of each candidate natively; the first failure in model order fails + /// the analysis. fn native_enrichments( &self, candidates: &[(usize, Shapes, bool)], - ) -> Result>, String> { + ) -> Result, String> { if candidates.is_empty() { return Ok(Vec::new()); } @@ -443,7 +356,23 @@ impl AnalysisSession { }) .collect(); let pool = self.catalog.native.analysis_pool()?; - Ok(pool.install(|| inputs.par_iter().map(enrichment).collect())) + let enrichments: Vec> = + pool.install(|| inputs.par_iter().map(enrichment).collect()); + enrichments + .into_iter() + .zip(&inputs) + .map(|(enrichment, input)| { + enrichment.map_err(|reason| { + if reason.starts_with(PYTHON_VALUE_ERROR) { + return reason; + } + native_failure( + &format!("{} enrichment", model_context(input.model)), + &reason, + ) + }) + }) + .collect() } fn input_schemas(&self, required: &[String]) -> Shapes { @@ -608,23 +537,16 @@ impl AnalysisSession { return; }; for model in models { - if cache.hits[*model] || cache.uncacheable[*model] { + if cache.hits[*model] { continue; } let (Some(key), Some(outcome)) = (cache.keys[*model], self.outcomes[*model].as_ref()) else { continue; }; - if let Some(bytes) = encode_outcome(outcome, cache.legacy_tables[*model].as_ref()) { - cache.store.put(key, bytes); - cache.stats.stored += 1; - } - } - } - - fn mark_uncacheable(&mut self, model: usize) { - if let Some(cache) = self.cache.as_mut() { - cache.uncacheable[model] = true; + let bytes: Vec = encode_outcome(outcome, cache.legacy_tables[*model].as_ref()); + cache.store.put(key, bytes); + cache.stats.stored += 1; } } @@ -766,41 +688,11 @@ fn legacy_outcome( } } -fn python_analysis(result: DeferredAnalysis) -> ModelAnalysis { - ModelAnalysis { - analysis_succeeded: result.analysis_succeeded, - columns: result.columns, - lineage: LineageFacts::PythonAnalysis, - has_star: result.has_star, - star_resolved: result.star_resolved, - binding_diagnostics: result.binding_diagnostics, - binding_validated: result.binding_validated, - } -} - -impl Deferral { - /// The deferred model's request index. - pub fn model(&self) -> usize { - match self { - Self::Analysis { model, .. } | Self::Enrichment { model, .. } => *model, - } - } - - /// Python's name for the deferral's kind. - pub fn kind(&self) -> &'static str { - match self { - Self::Analysis { .. } => DEFERRAL_ANALYSIS, - Self::Enrichment { .. } => DEFERRAL_ENRICHMENT, - } - } -} - /// Python's merge of a re-analysis with known inputs into the model's analysis. fn enriched_analysis( analysis: &ModelAnalysis, - enriched: DeferredAnalysis, + enriched: Enrichment, star_pending: bool, - lineage: LineageFacts, ) -> ModelAnalysis { let mut recovered: HashMap = HashMap::new(); for column in enriched @@ -820,9 +712,9 @@ fn enriched_analysis( ModelAnalysis { analysis_succeeded: enriched.analysis_succeeded, columns: enriched.columns, - lineage, + lineage: LineageFacts::NativeFacts(enriched.lineage), has_star: enriched.has_star, - star_resolved: enriched.star_resolved, + star_resolved: false, binding_diagnostics: analysis.binding_diagnostics.clone(), binding_validated: analysis.binding_validated, } @@ -840,30 +732,9 @@ fn enriched_analysis( merged } -/// A native re-analysis as Python's successful answer. -fn native_answer( - model: usize, - analysis_succeeded: bool, - columns: Option>, - has_star: bool, -) -> DeferredAnalysis { - DeferredAnalysis { - model, - analysis_succeeded, - columns, - has_star, - star_resolved: false, - binding_diagnostics: Vec::new(), - binding_validated: false, - } -} - /// A compiled model's output names and lineage from a successful analysis with native lineage. fn session_model_facts(analysis: &ModelAnalysis) -> Option { - let rows: &Vec = match &analysis.lineage { - LineageFacts::Native(rows) | LineageFacts::NativeFacts(rows) => rows, - LineageFacts::PythonAnalysis | LineageFacts::PythonEnrichment => return None, - }; + let (LineageFacts::Native(rows) | LineageFacts::NativeFacts(rows)) = &analysis.lineage; analysis.analysis_succeeded.then(|| SessionModelFacts { columns: analysis.columns.as_deref().map(column_names), lineage: rows.iter().map(output_sources).collect(), @@ -882,3 +753,8 @@ fn output_sources(row: &LineageRow) -> (String, Vec<(String, String)>) { } (row.output_column.clone(), sources) } + +/// The failure context naming one model. +fn model_context(model: &ModelRequest) -> String { + format!("{SESSION_CONTEXT} of model '{}'", model.name) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index 601ed4b493..9356af92f7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -6,6 +6,8 @@ pub(crate) const LEGACY_FALLBACK: &str = "native project type recovery requires pub(crate) const INVALID_NATIVE_RESPONSE: &str = "invalid native response"; /// Python's `UNKNOWN` column type. pub(crate) const UNKNOWN_TYPE: &str = "UNKNOWN"; +/// The wheel's `analyze_query` message for options it cannot read, such as a dialect alias. +pub(crate) const INVALID_ANALYZE_OPTIONS: &str = "Invalid analyze_query options object: "; /// Python's `InferredNullability.UNKNOWN`. pub(crate) const UNKNOWN_NULLABILITY: &str = "unknown"; /// Python's `InferredNullability` values by native code. @@ -37,9 +39,6 @@ pub(crate) const DIALECT_ALIASES: [(&str, &str); 3] = [ ]; /// The quote that marks an exact identifier. pub(crate) const QUOTED_IDENTIFIER_DELIMITER: char = '"'; -/// Python's names for a legacy-analysis and an enrichment deferral. -pub(crate) const DEFERRAL_ANALYSIS: &str = "analysis"; -pub(crate) const DEFERRAL_ENRICHMENT: &str = "enrichment"; /// Python's `ColumnTransformKind` compact codes. pub(crate) const TRANSFORM_DIRECT: u8 = 0; pub(crate) const TRANSFORM_CAST: u8 = 1; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index b65df1707f..1a7af5fe7b 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -5,7 +5,6 @@ pub mod finished_fact_models; pub mod finished_model_facts; pub mod prove_dynamic_contracts; pub mod prove_finished_dynamic_contracts; -pub mod provide_deferred_analyses; pub mod recover_cte_facts; pub mod run_analysis_session; pub mod session_sharing; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs index bbbb199898..00af66fa58 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs @@ -1,10 +1,12 @@ //! Python's `analyze_dynamic_column_contract` for models no analysis session analysed. +use sqlbuild_core::constants::PANIC_MESSAGE; + use crate::assembly::analysis_session::_helpers::dynamic_pivot::pivot_outcomes; use crate::assembly::analysis_session::constants::PIVOT_WORKER_STACK_BYTES; use crate::assembly::analysis_session::models::{PivotBatchRequest, PivotOutcome}; -/// Each model's proof in order; a model whose proof panics is deferred to Python. +/// Each model's proof in order; an internal failure of any proof fails them all. pub fn prove_dynamic_contracts(request: &PivotBatchRequest) -> Result, String> { std::thread::scope(|scope| { std::thread::Builder::new() @@ -12,6 +14,6 @@ pub fn prove_dynamic_contracts(request: &PivotBatchRequest) -> Result Result, String> { let pool = session.pool.as_ref().map_err(Clone::clone)?; - Ok(pooled_pivot_outcomes(pool, models, &session.tables)) + pooled_pivot_outcomes(pool, models, &session.tables) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/provide_deferred_analyses.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/provide_deferred_analyses.rs deleted file mode 100644 index ba205cd7c0..0000000000 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/provide_deferred_analyses.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Resume a native model analysis session with Python's answers. - -use crate::assembly::analysis_session::models::{AnalysisSession, DeferredAnalysis}; - -/// Answer the deferrals of the session's last step. -pub fn provide_deferred_analyses( - session: &mut AnalysisSession, - results: Vec, -) -> Result<(), String> { - session.provide(results) -} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/run_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/run_analysis_session.rs index 0c3c2a5c68..9176377634 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/run_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/run_analysis_session.rs @@ -1,8 +1,8 @@ -//! Advance a native model analysis session to its next deferrals. +//! Run a native model analysis session over every wave. use crate::assembly::analysis_session::models::{AnalysisSession, SessionStep}; -/// Advance until Python must answer deferrals; a step without deferrals means done. +/// Analyse every wave; any internal native failure fails the whole analysis. pub fn run_analysis_session(session: &mut AnalysisSession) -> Result { session.run() } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs index 3d716c5fe3..91e547cc5a 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs @@ -3,10 +3,14 @@ use crate::assembly::analysis_session::models::{AnalysisSession, SessionRequest}; use crate::semantic_validation::models::ProjectCatalog; -/// A session over `request` on a copy of `catalog`, or None where Python must analyse. +/// A session over `request` on a copy of `catalog`. +/// +/// # Errors +/// +/// An internal native failure where the request breaks the session's invariants. pub fn start_analysis_session( request: SessionRequest, catalog: &ProjectCatalog, -) -> Option { +) -> Result { AnalysisSession::start(request, catalog) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 26e3dd2773..6adceedf94 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -105,11 +105,10 @@ pub struct SessionModelFacts { pub lineage: OutputSources, } -/// A model's dynamic pivot proof: none declared, left to Python, or proven natively. +/// A model's dynamic pivot proof: none declared, or proven natively. #[derive(Debug, Clone, PartialEq, Eq)] pub enum PivotOutcome { Absent, - Deferred, Proof(ContractProof), } @@ -158,10 +157,6 @@ pub struct LineageRow { pub enum LineageFacts { /// Native compact rows. Native(Vec), - /// The lineage of the analysis Python returned for this model's analysis deferral. - PythonAnalysis, - /// The lineage of the analysis Python returned for this model's enrichment deferral. - PythonEnrichment, /// Plain lineage facts of a native re-analysis or legacy analysis. NativeFacts(Vec), } @@ -187,40 +182,11 @@ pub struct ModelOutcome { pub fused_binding_validated: bool, } -/// Work native analysis hands back to Python for one model. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Deferral { - /// The native engine asked for Python's legacy analysis of this model. - Analysis { - model: usize, - cleaned_sql: String, - binding_schema: Shapes, - binding_diagnostics: Vec, - /// Lineage Python projects from native rows, when the engine kept them. - lineage: Option>, - }, - /// Re-analysis with the model's known input shapes. - Enrichment { model: usize, input_schemas: Shapes }, -} - -/// Python's answer to one deferral, without the lineage Python keeps. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeferredAnalysis { - pub model: usize, - pub analysis_succeeded: bool, - pub columns: Option>, - pub has_star: bool, - pub star_resolved: bool, - pub binding_diagnostics: Vec, - pub binding_validated: bool, -} - -/// One phase of the session: the deferrals Python must answer, or none when it is done. +/// The finished run of the session: what Python records from it. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct SessionStep { - /// Shapes published since the previous step, in publication order. + /// Shapes published by the run, in publication order. pub publications: Shapes, - pub deferrals: Vec, /// Native analysis failures Python logs at debug level. pub failures: Vec, } @@ -254,26 +220,12 @@ pub struct ExpressionShapeRequest { pub expressions: Vec, } -/// Deferrals the session waits on before it continues a wave. -#[derive(Debug)] -pub(crate) enum Awaiting { - Analyses { - wave: usize, - models: Vec, - }, - Enrichments { - wave: usize, - star_pending: HashMap, - }, -} - /// Where the session is within its waves. #[derive(Debug)] pub(crate) enum Phase { Analyze, Complete(usize), Finish(usize), - Await(Awaiting), Done, } @@ -294,13 +246,12 @@ pub struct AnalysisCache { pub(crate) keys: Vec>, pub(crate) hits: Vec, /// Models whose outcome must not be stored: a logged failure or Python's answer. - pub(crate) uncacheable: Vec, /// The whole-table digest each legacy analysis read; its entry also requires it. pub(crate) legacy_tables: Vec>, pub(crate) stats: AnalysisCacheStats, } -/// One compile's model analysis, advanced phase by phase and resumed with Python's answers. +/// One compile's model analysis, advanced phase by phase. #[derive(Debug)] pub struct AnalysisSession { pub(crate) request: SessionRequest, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 2923472b07..189e387593 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -12,15 +12,13 @@ use crate::assembly::analysis_session::main::attach_analysis_cache::attach_analy use crate::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; use crate::assembly::analysis_session::main::finished_fact_models::finished_fact_models; use crate::assembly::analysis_session::main::finished_model_facts::finished_model_facts; -use crate::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_session; use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::main::take_analysis_cache::take_analysis_cache; use crate::assembly::analysis_session::models::{ - AnalysisCacheStats, AnalysisSession, ColumnFact, ContractProof, Deferral, DeferredAnalysis, - DynamicFamily, FinishedSession, LineageRow, ModelOutcome, ModelReference, ModelRequest, - PivotBatchRequest, PivotModel, PivotOutcome, PivotTables, SessionModelFacts, SessionOutcome, - SessionRequest, SessionStep, + AnalysisCacheStats, AnalysisSession, ColumnFact, ContractProof, DynamicFamily, FinishedSession, + LineageRow, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, + PivotOutcome, PivotTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::tests::test_types::{CachedRun, ModelSpec, RecoveredFacts}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -136,37 +134,24 @@ pub(crate) fn orders_request(models: Vec) -> SessionRequest { pub(crate) fn started(request: SessionRequest) -> AnalysisSession { let catalog: ProjectCatalog = catalog(&request.dialect, &request.catalog_schemas); - start_analysis_session(request, &catalog).expect("the models form no cycle") + start_analysis_session(request, &catalog).expect("the session starts") } -/// Run to completion, answering every deferral with `answer`: the steps, outcomes and session. +/// Run to completion: the step, outcomes and session. pub(crate) fn completed( session: AnalysisSession, - answer: fn(&Deferral) -> DeferredAnalysis, ) -> (Vec, Vec, FinishedSession) { - let (steps, outcome, finished, ()) = completed_with(session, answer, |_| ()); + let (steps, outcome, finished, ()) = completed_with(session, |_| ()); (steps, outcome.models, finished) } /// [`completed`], reading the session with `before_finish` once every wave has run. pub(crate) fn completed_with( mut session: AnalysisSession, - answer: fn(&Deferral) -> DeferredAnalysis, before_finish: impl FnOnce(&mut AnalysisSession) -> T, ) -> (Vec, SessionOutcome, FinishedSession, T) { - let mut done: bool = false; - let steps: Vec = std::iter::from_fn(|| { - (!done).then(|| { - let step: SessionStep = run_analysis_session(&mut session).expect("the session runs"); - let answers: Vec = step.deferrals.iter().map(answer).collect(); - done = answers.is_empty(); - (!done).then(|| { - provide_deferred_analyses(&mut session, answers).expect("the answers match") - }); - step - }) - }) - .collect(); + let steps: Vec = + vec![run_analysis_session(&mut session).expect("the session runs")]; let read: T = before_finish(&mut session); let (outcome, finished) = finish_analysis_session(session).expect("the session finished"); (steps, outcome, finished, read) @@ -177,7 +162,7 @@ pub(crate) fn cached_run(request: SessionRequest, store: NativeStore) -> CachedR let mut session: AnalysisSession = started(request); attach_analysis_cache(&mut session, store); let (steps, outcome, finished, (keys, hits, (store, stats))) = - completed_with(session, empty_answer, |session| { + completed_with(session, |session| { let (keys, hits) = session .cache .as_ref() @@ -201,7 +186,7 @@ pub(crate) fn cached_run(request: SessionRequest, store: NativeStore) -> CachedR /// The catalog changes and fact models of `request` run without a cache. pub(crate) fn uncached_catalog(request: SessionRequest) -> (Shapes, Vec, Vec) { - let (_, outcome, finished, ()) = completed_with(started(request), empty_answer, |_| ()); + let (_, outcome, finished, ()) = completed_with(started(request), |_| ()); catalog_changes(outcome, &finished) } @@ -245,20 +230,7 @@ pub(crate) fn described(outcome: &ModelOutcome) -> Vec { lines } -/// A successful Python answer with no columns and no star. -pub(crate) fn empty_answer(deferral: &Deferral) -> DeferredAnalysis { - DeferredAnalysis { - model: deferral.model(), - analysis_succeeded: true, - columns: None, - has_star: false, - star_resolved: false, - binding_diagnostics: Vec::new(), - binding_validated: true, - } -} - -/// `publish name col:TYPE ...` per publication, then `defer kind model` per deferral. +/// `publish name col:TYPE ...` per publication. pub(crate) fn step_lines(step: &SessionStep) -> Vec { let mut lines: Vec = Vec::new(); for (name, shape) in &step.publications { @@ -268,11 +240,6 @@ pub(crate) fn step_lines(step: &SessionStep) -> Vec { .collect(); lines.push(format!("publish {name} {}", columns.join(" "))); } - lines.extend( - step.deferrals - .iter() - .map(|deferral| format!("defer {} {}", deferral.kind(), deferral.model())), - ); lines } @@ -285,10 +252,7 @@ pub(crate) fn model_requests(models: &[ModelSpec]) -> Vec { /// Run `models` over the orders request: each step's lines and each model's description. pub(crate) fn session_lines(models: &[ModelSpec]) -> (Vec>, Vec>) { - let (steps, outcomes, _) = completed( - started(orders_request(model_requests(models))), - empty_answer, - ); + let (steps, outcomes, _) = completed(started(orders_request(model_requests(models)))); ( steps.iter().map(step_lines).collect(), outcomes.iter().map(described).collect(), @@ -297,10 +261,7 @@ pub(crate) fn session_lines(models: &[ModelSpec]) -> (Vec>, Vec Vec { - let (_, _, finished) = completed( - started(orders_request(model_requests(models))), - empty_answer, - ); + let (_, _, finished) = completed(started(orders_request(model_requests(models)))); models .iter() .map(|(name, ..)| { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs index 7ff569e36b..c08f2a556c 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs @@ -354,7 +354,7 @@ fn given_any_analysis_input_changed_when_keying_then_the_key_changes() { }, CacheKeyTestCase { description: "dialect", - change: |request| request.dialect = "postgres".to_owned(), + change: |request| request.dialect = "snowflake".to_owned(), expected_key_changed: true, }, CacheKeyTestCase { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index cc144aa743..3a5f6069de 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -1,5 +1,6 @@ use crate::assembly::analysis_session::main::prove_dynamic_contracts::prove_dynamic_contracts; use crate::assembly::analysis_session::main::prove_finished_dynamic_contracts::prove_finished_dynamic_contracts; +use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_session; use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ @@ -7,12 +8,20 @@ use crate::assembly::analysis_session::tests::helpers::{ orders_request, pivot_request, proven, recovered_facts, session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ - CteRecoveryTestCase, LegacyAnalysisTestCase, PivotTestCase, SessionFactsTestCase, - SessionTestCase, UnscheduledTestCase, + CteRecoveryTestCase, LegacyAnalysisTestCase, ModelSpec, PivotTestCase, SessionFactsTestCase, + SessionFailureTestCase, SessionTestCase, UnscheduledTestCase, }; +/// A model whose missing column stays untyped over a known input, so completion re-analyses it. +const UNTYPED_ORDERS: ModelSpec = ( + "orders_untyped", + "SELECT order_id, missing_column FROM __source(\"raw_orders\")", + &["raw_orders"], + &[], +); + #[test] -fn given_models_when_running_the_session_then_defers_and_publishes_as_python_does() { +fn given_models_when_running_the_session_then_publishes_as_python_does() { let test_cases = [ SessionTestCase { description: "typed producers publish closed shapes their consumers bind", @@ -245,7 +254,7 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe } #[test] -fn given_models_when_starting_then_only_duplicate_names_leave_analysis_to_python() { +fn given_models_when_starting_then_only_duplicate_names_fail_as_an_internal_error() { let test_cases = [ UnscheduledTestCase { description: "models that reference each other analyse in one unordered wave", @@ -263,7 +272,7 @@ fn given_models_when_starting_then_only_duplicate_names_leave_analysis_to_python &["orders"], ), ], - expected_started: true, + expected_error: None, }, UnscheduledTestCase { description: "two models with one name", @@ -271,7 +280,9 @@ fn given_models_when_starting_then_only_duplicate_names_leave_analysis_to_python ("orders", "SELECT 1 AS order_id", &[], &[]), ("orders", "SELECT 2 AS order_id", &[], &[]), ], - expected_started: false, + expected_error: Some( + "NativeCompilerError: native model analysis: two analysed models share a name", + ), }, ]; for test_case in test_cases { @@ -279,8 +290,45 @@ fn given_models_when_starting_then_only_duplicate_names_leave_analysis_to_python let catalog = catalog(&request.dialect, &request.catalog_schemas); assert_eq!( - start_analysis_session(request, &catalog).is_some(), - test_case.expected_started, + start_analysis_session(request, &catalog).err().as_deref(), + test_case.expected_error, + "{}", + test_case.description + ); + } +} + +#[test] +fn given_enrichment_dialect_when_running_then_answers_or_raises_python_value_error() { + let test_cases = [ + SessionFailureTestCase { + description: "an analysis dialect name the query analysis reads", + dialect: "duckdb", + models: &[UNTYPED_ORDERS], + expected_error: None, + }, + SessionFailureTestCase { + description: "an adapter dialect alias Python's analyze_query options reject", + dialect: "postgres", + models: &[UNTYPED_ORDERS], + expected_error: Some( + "ValueError: Invalid analyze_query options object: unknown variant `postgres`", + ), + }, + ]; + for test_case in test_cases { + let mut request = orders_request(model_requests(test_case.models)); + request.dialect = test_case.dialect.to_owned(); + let catalog = catalog(&request.dialect, &request.catalog_schemas); + let mut session = start_analysis_session(request, &catalog).expect("the session starts"); + + let error: Option = run_analysis_session(&mut session).err(); + + assert_eq!( + error + .as_deref() + .map(|error| error.split(',').next().unwrap_or(error)), + test_case.expected_error, "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs index e55e9e91fc..41521e1cdc 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs @@ -27,7 +27,14 @@ pub(crate) struct SessionFactsTestCase { pub(crate) struct UnscheduledTestCase { pub(crate) description: &'static str, pub(crate) models: &'static [ModelSpec], - pub(crate) expected_started: bool, + pub(crate) expected_error: Option<&'static str>, +} + +pub(crate) struct SessionFailureTestCase { + pub(crate) description: &'static str, + pub(crate) dialect: &'static str, + pub(crate) models: &'static [ModelSpec], + pub(crate) expected_error: Option<&'static str>, } pub(crate) struct ExpressionShapeTestCase { diff --git a/crates/sqlbuild-core/src/constants.rs b/crates/sqlbuild-core/src/constants.rs index 423634b200..d0ab0de4b6 100644 --- a/crates/sqlbuild-core/src/constants.rs +++ b/crates/sqlbuild-core/src/constants.rs @@ -4,3 +4,7 @@ pub const OPEN_PAREN: &str = "("; pub const CLOSE_PAREN: &str = ")"; /// The error text every caught native panic carries across the boundary. pub const PANIC_MESSAGE: &str = "NativeCompilerError: native SQL compilation panicked"; +/// The prefix every native internal failure carries, so the boundary raises `NativeCompilerError`. +pub const NATIVE_FAILURE_PREFIX: &str = "NativeCompilerError: "; +/// The prefix of a native failure Python raised as `ValueError`, with the rest as its message. +pub const PYTHON_VALUE_ERROR: &str = "ValueError: "; diff --git a/crates/sqlbuild-core/src/panics/main/is_compiler_panic.rs b/crates/sqlbuild-core/src/panics/main/is_compiler_panic.rs index 51b2e937c2..caa8db3575 100644 --- a/crates/sqlbuild-core/src/panics/main/is_compiler_panic.rs +++ b/crates/sqlbuild-core/src/panics/main/is_compiler_panic.rs @@ -1,5 +1,6 @@ -use crate::constants::PANIC_MESSAGE; +use crate::constants::NATIVE_FAILURE_PREFIX; +/// Whether `message` is a caught panic or another native internal failure. pub fn is_compiler_panic(message: &str) -> bool { - message == PANIC_MESSAGE + message.starts_with(NATIVE_FAILURE_PREFIX) } diff --git a/crates/sqlbuild-core/src/panics/main/mod.rs b/crates/sqlbuild-core/src/panics/main/mod.rs index c40fe7bcb0..b25c155466 100644 --- a/crates/sqlbuild-core/src/panics/main/mod.rs +++ b/crates/sqlbuild-core/src/panics/main/mod.rs @@ -1,2 +1,3 @@ pub mod catch_compiler_panic; pub mod is_compiler_panic; +pub mod native_failure; diff --git a/crates/sqlbuild-core/src/panics/main/native_failure.rs b/crates/sqlbuild-core/src/panics/main/native_failure.rs new file mode 100644 index 0000000000..97efa25b2d --- /dev/null +++ b/crates/sqlbuild-core/src/panics/main/native_failure.rs @@ -0,0 +1,11 @@ +use crate::constants::NATIVE_FAILURE_PREFIX; + +/// An internal native failure the boundary raises as `NativeCompilerError`; a caught panic keeps +/// its own message. +pub fn native_failure(context: &str, reason: &str) -> String { + if reason.starts_with(NATIVE_FAILURE_PREFIX) { + format!("{reason} ({context})") + } else { + format!("{NATIVE_FAILURE_PREFIX}{context}: {reason}") + } +} diff --git a/crates/sqlbuild-core/src/panics/tests/test_panics.rs b/crates/sqlbuild-core/src/panics/tests/test_panics.rs index f0992c1245..ce02e2ff2c 100644 --- a/crates/sqlbuild-core/src/panics/tests/test_panics.rs +++ b/crates/sqlbuild-core/src/panics/tests/test_panics.rs @@ -1,5 +1,6 @@ use crate::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::panics::main::is_compiler_panic::is_compiler_panic; +use crate::panics::main::native_failure::native_failure; use crate::panics::tests::test_types::PanicTestCase; #[test] @@ -11,6 +12,16 @@ fn given_native_failure_when_crossing_python_boundary_then_only_unwinds_become_n operation: || std::panic::panic_any("synthetic compiler failure"), expected_named_error: true, }, + PanicTestCase { + description: "an internal native failure is a named compiler error", + operation: || { + Err(native_failure( + "model analysis of 'orders'", + "a broken invariant", + )) + }, + expected_named_error: true, + }, PanicTestCase { description: "ordinary compiler error keeps its own message", operation: || Err("unknown column 'customer_id'".to_owned()), diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 808d0fd741..137b2e1c8b 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -1,6 +1,7 @@ //! The native model analysis session and expression-source shapes for the preview engine. -use pyo3::prelude::{Bound, Py, PyAny, PyModule, PyModuleMethods, PyRef, PyResult, Python}; +use pyo3::exceptions::PyValueError; +use pyo3::prelude::{Bound, Py, PyAny, PyErr, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::types::PyDict; use pyo3::{pyclass, pyfunction, pymethods, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::main::attach_analysis_cache::attach_analysis_cache; @@ -9,22 +10,24 @@ use sqlbuild_analysis::assembly::analysis_session::main::finish_analysis_session use sqlbuild_analysis::assembly::analysis_session::main::finished_fact_models::finished_fact_models; use sqlbuild_analysis::assembly::analysis_session::main::prove_dynamic_contracts::prove_dynamic_contracts; use sqlbuild_analysis::assembly::analysis_session::main::prove_finished_dynamic_contracts::prove_finished_dynamic_contracts; -use sqlbuild_analysis::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; use sqlbuild_analysis::assembly::analysis_session::main::run_analysis_session::run_analysis_session; use sqlbuild_analysis::assembly::analysis_session::main::session_sharing::session_sharing; use sqlbuild_analysis::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use sqlbuild_analysis::assembly::analysis_session::main::take_analysis_cache::take_analysis_cache; use sqlbuild_analysis::assembly::analysis_session::models::{ - AnalysisCacheStats, AnalysisSession, ColumnFact, Deferral, DeferredAnalysis, DynamicFamily, - ExpressionShape, ExpressionShapeRequest, FinishedSession, LineageFacts, LineageRow, - ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, PivotOutcome, - PivotTables, SessionOutcome, SessionRequest, SessionStep, + AnalysisCacheStats, AnalysisSession, ColumnFact, DynamicFamily, ExpressionShape, + ExpressionShapeRequest, FinishedSession, LineageFacts, LineageRow, ModelOutcome, + ModelReference, ModelRequest, PivotBatchRequest, PivotModel, PivotOutcome, PivotTables, + SessionOutcome, SessionRequest, SessionStep, }; use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; use sqlbuild_analysis::semantic_validation::types::DiagnosticRow; use sqlbuild_cache::store::models::NativeStore; +use sqlbuild_core::constants::{PANIC_MESSAGE, PYTHON_VALUE_ERROR}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use sqlbuild_core::panics::main::is_compiler_panic::is_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; use std::path::PathBuf; use crate::bindings::_helpers::analysis_session::nullability_rules::{ @@ -69,27 +72,8 @@ type RequestRow = ( Vec<(String, Vec)>, Vec, ); -/// `(kind, model, cleaned_sql, schemas, binding diagnostics, lineage)`. -type DeferralRow = ( - &'static str, - usize, - Option, - Shapes, - Vec, - Option>, -); -/// `(publications, deferrals, failures)`. -type StepRow = (Shapes, Vec, Vec); -/// `(model, succeeded, columns, has_star, star_resolved, binding diagnostics, validated)`. -type DeferredRow = ( - usize, - bool, - Option>, - bool, - bool, - Vec, - bool, -); +/// `(publications, failures)`. +type StepRow = (Shapes, Vec); /// One model's analysis, lineage source and cleaned SQL. type OutcomeRow = ( bool, @@ -129,18 +113,19 @@ type CacheStatsRow = (usize, usize, usize, Option); /// `(models, catalog schema additions, analysis-shape names, dynamic pivot proofs)`. type FinishRow = (Vec, Shapes, Vec, Vec); -/// One compile's native model analysis; any failure hands the whole analysis back to Python. +/// One compile's native model analysis; an internal failure raises `NativeCompilerError`. #[pyclass(module = "sqlbuild._native")] pub(crate) struct NativeModelAnalysisSession { inner: Option, finished: Option, - failure: Option, sharing: (usize, usize), cache_path: Option, cache_stats: Option, rule_failure: RuleFailure, } +/// What an internal native failure of the session names. +const SESSION_CONTEXT: &str = "native model analysis"; /// The native store kind holding finished model analyses. const MODEL_ANALYSIS_STORE_KIND: &str = "model-analyses"; @@ -150,17 +135,24 @@ impl NativeModelAnalysisSession { self.finished.as_ref() } - /// Keep `result`'s value, or end the session and keep its failure for Python's debug log. - fn kept(&mut self, result: Result) -> Option { - match result { - Ok(value) => Some(value), - Err(error) => { - self.inner = None; - self.failure = Some(error); - None - } - } + /// The running session, or `NativeCompilerError` once it has failed or finished. + fn running(&mut self) -> PyResult { + self.inner + .take() + .ok_or_else(|| internal_error("the session is not running")) + } +} + +/// An internal native failure of the session as `NativeCompilerError`, or the `ValueError` +/// Python's analysis raised for the same input. +fn internal_error(reason: &str) -> PyErr { + if let Some(message) = reason.strip_prefix(PYTHON_VALUE_ERROR) { + return PyValueError::new_err(message.to_owned()); + } + if is_compiler_panic(reason) && reason != PANIC_MESSAGE { + return compiler_error(reason); } + compiler_error(native_failure(SESSION_CONTEXT, reason)) } #[pymethods] @@ -174,11 +166,10 @@ impl NativeModelAnalysisSession { .unwrap_or_default() } - /// Advance; None means Python must analyse every model, no deferrals means done. - fn run(&mut self, py: Python<'_>) -> PyResult> { - let Some(mut session) = self.inner.take() else { - return Ok(None); - }; + /// Analyse every model; a model's adapter rule exception is raised as it was, any internal + /// native failure as `NativeCompilerError`. + fn run(&mut self, py: Python<'_>) -> PyResult { + let mut session: AnalysisSession = self.running()?; let result: Result<(AnalysisSession, SessionStep), String> = py.compiler_detach(move || { run_analysis_session(&mut session).map(|step| (session, step)) @@ -186,32 +177,18 @@ impl NativeModelAnalysisSession { if let Some(error) = raised(&self.rule_failure) { return Err(error); } - let Some((session, step)) = self.kept(result) else { - return Ok(None); - }; + let (session, step) = result.map_err(|reason| internal_error(&reason))?; self.sharing = session_sharing(&session); self.inner = Some(session); - Ok(Some(step_row(step))) - } - - /// Answer the last step's deferrals; False means Python must analyse every model. - fn provide(&mut self, results: Vec) -> bool { - let Some(mut session) = self.inner.take() else { - return false; - }; - let answers: Vec = results.into_iter().map(deferred_analysis).collect(); - let provided: Result<(), String> = - catch_compiler_panic(|| provide_deferred_analyses(&mut session, answers)); - self.inner = Some(session); - self.kept(provided).is_some() + Ok(step_row(step)) } - /// Every model's outcome once done, or None for Python; saves the cache best-effort. - fn finish(&mut self, py: Python<'_>) -> Option { - let mut session: AnalysisSession = self.inner.take()?; + /// Every model's outcome; saves the cache best-effort. + fn finish(&mut self, py: Python<'_>) -> PyResult { + let mut session: AnalysisSession = self.running()?; let cache: Option<(NativeStore, AnalysisCacheStats)> = take_analysis_cache(&mut session); - let outcome = catch_compiler_panic(|| finish_analysis_session(session)); - let (outcome, finished) = self.kept(outcome)?; + let (outcome, finished) = catch_compiler_panic(|| finish_analysis_session(session)) + .map_err(|reason| internal_error(&reason))?; self.finished = Some(finished); if let (Some((store, stats)), Some(path)) = (cache, self.cache_path.as_ref()) { let saved: Option = save_store(py, &store, path, &[]) @@ -219,7 +196,7 @@ impl NativeModelAnalysisSession { .map(|error| error.to_string()); self.cache_stats = Some((stats.hits, stats.misses, stats.stored, saved)); } - Some(finish_row(outcome)) + Ok(finish_row(outcome)) } /// The analysis cache's `(hits, misses, stored, failure)`; None without a cache. @@ -228,24 +205,21 @@ impl NativeModelAnalysisSession { self.cache_stats.clone() } - /// Each model's dynamic pivot proof from the finished session's tables, or None for Python. + /// Each model's dynamic pivot proof from the finished session's tables. fn prove_dynamic_contracts( &self, py: Python<'_>, models: Vec, - ) -> Option> { - let session: &FinishedSession = self.finished.as_ref()?; + ) -> PyResult> { + let session: &FinishedSession = self + .finished + .as_ref() + .ok_or_else(|| internal_error("the session has not finished"))?; let models: Vec = models.into_iter().map(pivot_model).collect(); - match py.compiler_detach(|| prove_finished_dynamic_contracts(session, &models)) { - Ok(outcomes) => Some(outcomes.into_iter().map(contract_row).collect()), - Err(_) => None, - } - } - - /// Why the session handed the analysis back to Python, when it did. - #[getter] - fn failure(&self) -> Option { - self.failure.clone() + let outcomes: Vec = py + .compiler_detach(|| prove_finished_dynamic_contracts(session, &models)) + .map_err(|reason| internal_error(&reason))?; + Ok(outcomes.into_iter().map(contract_row).collect()) } /// `(shared members, shared members re-analysed alone)` as of the last step. @@ -255,7 +229,7 @@ impl NativeModelAnalysisSession { } } -/// Start a session on `catalog`, or None where Python must analyse. +/// Start a session on `catalog`. /// /// `adapter_rules` holds the adapter's own nullability rules by name, with the /// `InferredNullability` type they take and return; the request names them `python`. @@ -267,18 +241,15 @@ fn start_model_analysis_session( request: RequestRow, cache: Option<(PathBuf, String)>, adapter_rules: Option<(Py, Py)>, -) -> PyResult> { +) -> PyResult { let mut request: SessionRequest = session_request(request); let rule_failure: RuleFailure = RuleFailure::default(); request.nullability_callback = adapter_rules .map(|(rules, nullability)| nullability_callback(rules, nullability, rule_failure.clone())); let catalog = &catalog.inner; - let session: Option = py - .compiler_detach(|| Ok(start_analysis_session(request, catalog))) - .map_err(compiler_error)?; - let Some(mut session) = session else { - return Ok(None); - }; + let mut session: AnalysisSession = py + .compiler_detach(|| start_analysis_session(request, catalog)) + .map_err(|reason| internal_error(&reason))?; let mut cache_path: Option = None; let mut cache_stats: Option = None; if let Some((path, environment)) = cache { @@ -290,15 +261,14 @@ fn start_model_analysis_session( Err(error) => cache_stats = Some((0, 0, 0, Some(error.to_string()))), } } - Ok(Some(NativeModelAnalysisSession { + Ok(NativeModelAnalysisSession { inner: Some(session), finished: None, - failure: None, sharing: (0, 0), cache_path, cache_stats, rule_failure, - })) + }) } /// Each expression's shape, with `False` where Python must infer it. @@ -396,56 +366,7 @@ fn model_request(row: ModelRow) -> ModelRequest { } fn step_row(step: SessionStep) -> StepRow { - ( - step.publications, - step.deferrals.into_iter().map(deferral_row).collect(), - step.failures, - ) -} - -fn deferral_row(deferral: Deferral) -> DeferralRow { - let kind: &'static str = deferral.kind(); - match deferral { - Deferral::Analysis { - model, - cleaned_sql, - binding_schema, - binding_diagnostics, - lineage, - } => ( - kind, - model, - Some(cleaned_sql), - binding_schema, - binding_diagnostics, - lineage.map(lineage_items), - ), - Deferral::Enrichment { - model, - input_schemas, - } => (kind, model, None, input_schemas, Vec::new(), None), - } -} - -fn deferred_analysis(row: DeferredRow) -> DeferredAnalysis { - let (model, analysis_succeeded, columns, has_star, star_resolved, diagnostics, validated) = row; - DeferredAnalysis { - model, - analysis_succeeded, - columns: columns.map(|columns| columns.into_iter().map(column_fact).collect()), - has_star, - star_resolved, - binding_diagnostics: diagnostics, - binding_validated: validated, - } -} - -fn column_fact((name, data_type, nullability): ColumnRow) -> ColumnFact { - ColumnFact { - name, - data_type, - nullability, - } + (step.publications, step.failures) } fn column_row(column: ColumnFact) -> ColumnRow { @@ -499,7 +420,6 @@ fn dynamic_family(row: FamilyRow) -> DynamicFamily { fn contract_row(outcome: PivotOutcome) -> ContractRow { match outcome { PivotOutcome::Absent => ("absent", None), - PivotOutcome::Deferred => ("deferred", None), PivotOutcome::Proof(proof) => ( "proof", Some(( @@ -518,8 +438,6 @@ fn outcome_row(outcome: ModelOutcome) -> OutcomeRow { let analysis = outcome.analysis; let (source, lineage) = match analysis.lineage { LineageFacts::Native(rows) => ("native", lineage_items(rows)), - LineageFacts::PythonAnalysis => ("analysis", Vec::new()), - LineageFacts::PythonEnrichment => ("enrichment", Vec::new()), LineageFacts::NativeFacts(rows) => ("facts", lineage_items(rows)), }; ( @@ -537,12 +455,12 @@ fn outcome_row(outcome: ModelOutcome) -> OutcomeRow { ) } -/// Each model's dynamic pivot proof outside any session, or None for Python's proofs. +/// Each model's dynamic pivot proof outside any session. #[pyfunction] fn prove_dynamic_column_contracts( py: Python<'_>, request: PivotRequestRow, -) -> Option> { +) -> PyResult> { let (dialect, column_types, authoritative_types, column_nullability, by_table, models) = request; let request = PivotBatchRequest { @@ -555,10 +473,10 @@ fn prove_dynamic_column_contracts( }, models: models.into_iter().map(pivot_model).collect(), }; - match py.compiler_detach(|| prove_dynamic_contracts(&request)) { - Ok(outcomes) => Some(outcomes.into_iter().map(contract_row).collect()), - Err(_) => None, - } + let outcomes: Vec = py + .compiler_detach(|| prove_dynamic_contracts(&request)) + .map_err(|reason| internal_error(&reason))?; + Ok(outcomes.into_iter().map(contract_row).collect()) } fn pivot_model(row: PivotModelRow) -> PivotModel { diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py index 560870c25c..4ac0c0f2dc 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py @@ -5,15 +5,13 @@ from typing import Any import sqlbuild._native as _native -from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral from sqlbuild.compiler.analysis_session._helpers.session_rows import ( contract_proof, family_rows, shape_rows, ) -from sqlbuild.compiler.analysis_session.constants import CONTRACT_DEFERRED, DEFERRAL_DYNAMIC_PIVOT from sqlbuild.compiler.analysis_session.models import NativePivotTables -from sqlbuild.compiler.analysis_session.types import ContractRow, ShapeRows +from sqlbuild.compiler.analysis_session.types import ContractRow from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis from sqlbuild.compiler.compile.models import DynamicColumnContractProof from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily @@ -27,47 +25,27 @@ def native_dynamic_contracts( tables: NativePivotTables, models: tuple[tuple[str, tuple[SchemaDynamicColumnFamily, ...]], ...], ) -> tuple[DynamicColumnContractProof | None, ...]: - """Each model's proof in order, None (recorded) where Python must prove it.""" + """Each model's proof in order; a native internal failure raises.""" if not models: return () rows: list[_ModelRow] = [_model_row(sql=sql, families=families) for sql, families in models] - contracts: list[ContractRow] | None = ( + contracts: list[ContractRow] = ( session.prove_dynamic_contracts(rows) if session is not None - else _standalone_contracts(tables=tables, rows=rows) - ) - if contracts is None: - record_analysis_deferral(kind=DEFERRAL_DYNAMIC_PIVOT, count=len(models)) - return (None,) * len(models) - record_analysis_deferral( - kind=DEFERRAL_DYNAMIC_PIVOT, - count=sum(kind == CONTRACT_DEFERRED for kind, _ in contracts), + else _native.prove_dynamic_column_contracts( + ( + tables.dialect or "generic", + shape_rows(tables.column_types_by_table), + shape_rows(tables.authoritative_column_types_by_table), + shape_rows(tables.column_nullability_by_table), + family_rows(tables.dynamic_families_by_table), + rows, + ) + ) ) return tuple(contract_proof(proof) for _, proof in contracts) def _model_row(*, sql: str, families: tuple[SchemaDynamicColumnFamily, ...]) -> _ModelRow: return (sql, list(map(PythonModelAnalysis.family_row, families))) - - -def _standalone_contracts( - *, tables: NativePivotTables, rows: list[_ModelRow] -) -> list[ContractRow] | None: - shapes: tuple[ShapeRows | None, ...] = ( - shape_rows(tables.column_types_by_table), - shape_rows(tables.authoritative_column_types_by_table), - shape_rows(tables.column_nullability_by_table), - ) - if any(shape is None for shape in shapes): - return None - return _native.prove_dynamic_column_contracts( - ( - tables.dialect or "generic", - shapes[0] or [], - shapes[1] or [], - shapes[2] or [], - family_rows(tables.dynamic_families_by_table), - rows, - ) - ) diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py index 4f4cb3a29c..a081c392fa 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py @@ -1,4 +1,4 @@ -"""Start the native model analysis session, or hand the whole analysis to Python.""" +"""Run the native model analysis session.""" from __future__ import annotations @@ -14,9 +14,6 @@ from sqlbuild.compiler.analysis_session.classes.native_model_analysis import NativeModelAnalysis from sqlbuild.compiler.analysis_session.constants import ( ADAPTER_NULLABILITY_CALLBACK, - DEFERRAL_NO_CATALOG, - DEFERRAL_NO_COMPACT_ANALYSIS, - DEFERRAL_SESSION, NATIVE_ANALYSIS_STORE_FILE_NAME, NATIVE_ANALYSIS_STORE_VERSION, ) @@ -25,53 +22,39 @@ NativeModelAnalysisRequest, ) from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis -from sqlbuild.compiler.compile.models import AnalysisCacheContext, ModelSqlAnalysis +from sqlbuild.compiler.compile.models import AnalysisCacheContext from sqlbuild.compiler.frontier.main.compiled_code_identity import compiled_code_identity from sqlbuild.compiler.lineage.types import InferredNullability -def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses | None: - """Each model's analysis by name and the finished session, or None (recorded) for Python.""" +def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses: + """Each model's analysis by name and the finished session; a native internal failure raises.""" - catalog: Any = request.inference_profile.binding_catalog - if not request.allow_compact_analysis: - record_analysis_deferral(kind=DEFERRAL_NO_COMPACT_ANALYSIS) - return None - if catalog is None: - record_analysis_deferral(kind=DEFERRAL_NO_CATALOG) - return None if not request.model_inputs: return NativeModelAnalyses(analyses={}, session=None) + catalog: Any = request.inference_profile.binding_catalog python: PythonModelAnalysis = PythonModelAnalysis( model_inputs=request.model_inputs, inference_profile=request.inference_profile, complete_binding_schemas=request.complete_binding_schemas, ) - row: tuple[object, ...] | None = session_request( - request=request, python=python, schemas=catalog.schemas - ) adapter_rules: dict[str, FunctionNullabilityRule] = adapter_nullability_rules( request.inference_profile ) if adapter_rules: record_analysis_deferral(kind=ADAPTER_NULLABILITY_CALLBACK) - session: _native.NativeModelAnalysisSession | None = ( - _native.start_model_analysis_session( - catalog.native, - row, - None if adapter_rules else _analysis_store(request), - (adapter_rules, InferredNullability) if adapter_rules else None, - ) - if row is not None - else None + session: _native.NativeModelAnalysisSession = _native.start_model_analysis_session( + catalog.native, + session_request(request=request, python=python, schemas=catalog.schemas), + None if adapter_rules else _analysis_store(request), + (adapter_rules, InferredNullability) if adapter_rules else None, + ) + return NativeModelAnalyses( + analyses=NativeModelAnalysis(request=request, python=python, catalog=catalog).analyses( + session + ), + session=session, ) - if session is None: - record_analysis_deferral(kind=DEFERRAL_SESSION) - return None - analyses: dict[str, ModelSqlAnalysis] | None = NativeModelAnalysis( - request=request, python=python, catalog=catalog - ).analyses(session) - return None if analyses is None else NativeModelAnalyses(analyses=analyses, session=session) def _analysis_store(request: NativeModelAnalysisRequest) -> tuple[str, str] | None: diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 3b2cc41f3d..88afc03db3 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -20,7 +20,6 @@ from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( ColumnRow, - DeferredRow, DiagnosticRow, FamilyRow, LineageItem, @@ -35,7 +34,6 @@ DynamicColumnContractProof, DynamicColumnFamilyProof, InferredColumn, - PolyglotAnalysisResult, ) from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.sql_analysis.constants import CASE_SENSITIVE_BINDING_DIALECTS @@ -49,18 +47,13 @@ } -def shape_rows(shapes: Mapping[str, Mapping[str, object]]) -> ShapeRows | None: - """`{relation: {column: value}}` as ordered rows, or None when a value is not text.""" +def shape_rows(shapes: Mapping[str, Mapping[str, str]]) -> ShapeRows: + """`{relation: {column: value}}` as ordered rows; discovery rejects non-text names and types.""" - rows: ShapeRows = [] - for name, shape in shapes.items(): - columns: list[tuple[str, str]] = [] - for column, value in shape.items(): - if not isinstance(column, str) or not isinstance(value, str): - return None - columns.append((column, str(value))) - rows.append((name, columns)) - return rows + return [ + (name, [(column, str(value)) for column, value in shape.items()]) + for name, shape in shapes.items() + ] def session_request( @@ -68,18 +61,16 @@ def session_request( request: NativeModelAnalysisRequest, python: PythonModelAnalysis, schemas: Mapping[str, Mapping[str, str]], -) -> tuple[object, ...] | None: - """The session request, or None when a shape cannot cross as text.""" +) -> tuple[object, ...]: + """The session request.""" profile: ExpressionInferenceProfile = request.inference_profile - shapes: tuple[ShapeRows | None, ...] = ( + shapes: tuple[ShapeRows, ...] = ( shape_rows(request.column_types_by_table), shape_rows(request.column_nullability_by_table), shape_rows(request.complete_binding_schemas), shape_rows(schemas), ) - if any(rows is None for rows in shapes): - return None return ( profile.sql_analysis_dialect or "generic", case_sensitive_shapes(profile=profile, dialect=profile.sql_analysis_dialect), @@ -114,7 +105,7 @@ def adapter_nullability_rules( def contract_proof(row: ProofRow | None) -> DynamicColumnContractProof | None: - """A native dynamic pivot proof as Python's, None when absent or left to Python.""" + """A native dynamic pivot proof as Python's, None when absent.""" if row is None: return None @@ -159,20 +150,6 @@ def diagnostic_rows(diagnostics: tuple[SqlBindingDiagnostic, ...]) -> list[Diagn ] -def deferred_row(*, model: int, analysis: PolyglotAnalysisResult) -> DeferredRow: - """Python's answer to one deferral, without the lineage Python keeps.""" - - return ( - model, - analysis.analysis_succeeded, - column_rows(analysis.columns), - analysis.has_star, - analysis.star_resolved, - diagnostic_rows(analysis.binding_diagnostics), - analysis.binding_validated, - ) - - def inferred_columns(rows: list[ColumnRow] | None) -> tuple[InferredColumn, ...] | None: if rows is None: return None diff --git a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py index e3d4950bf6..9068596130 100644 --- a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py @@ -1,39 +1,26 @@ -"""One compile's native model analysis, with Python answering the session's deferrals.""" +"""One compile's native model analysis, read into Python's analysis objects.""" from __future__ import annotations import logging -from collections import Counter from collections.abc import Sequence from typing import Any import sqlbuild._native as _native -from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral from sqlbuild.compiler.analysis_session._helpers.session_rows import ( binding_diagnostics, compact_lineage, contract_proof, - deferred_row, lineage_facts, ) from sqlbuild.compiler.analysis_session.constants import ( - CONTRACT_DEFERRED, - DEFERRAL_ANALYSIS, - DEFERRAL_DYNAMIC_PIVOT, - DEFERRAL_ENRICHMENT, - DEFERRAL_LEGACY_ANALYSIS, - DEFERRAL_SESSION, - LINEAGE_FACTS, LINEAGE_NATIVE, NATIVE_ANALYSIS_FAILURE_MESSAGE, NATIVE_ANALYSIS_STORE_FAILURE_MESSAGE, - NATIVE_SESSION_FAILURE_MESSAGE, ) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( ColumnRow, - DeferralRow, - DeferredRow, FinishRow, OutcomeRow, ProofRow, @@ -55,7 +42,7 @@ class NativeModelAnalysis: - """Drive one native session, answering deferrals with today's Python analysis.""" + """Drive one native session and read its outcomes as Python's analyses.""" def __init__( self, @@ -70,40 +57,32 @@ def __init__( self._nullability: dict[str, dict[str, InferredNullability]] = dict( request.column_nullability_by_table ) - self._kept: dict[tuple[int, str], PolyglotAnalysisResult] = {} self._columns: dict[ColumnRow, InferredColumn] = {} - self._deferrals: Counter[str] = Counter() - def analyses( - self, session: _native.NativeModelAnalysisSession - ) -> dict[str, ModelSqlAnalysis] | None: - """Every model's analysis by name, or None when Python must analyse them all.""" + def analyses(self, session: _native.NativeModelAnalysisSession) -> dict[str, ModelSqlAnalysis]: + """Every model's analysis by name; a native internal failure raises.""" - finished: FinishRow | None = self._finished(session) - if finished is None: - record_analysis_deferral(kind=DEFERRAL_SESSION) + step: StepRow = session.run() + publications, failures = step + for failure in failures: log_debug_event( logger=_DEBUG_LOGGER, - message=NATIVE_SESSION_FAILURE_MESSAGE, - sqlbuild_error=session.failure, + message=NATIVE_ANALYSIS_FAILURE_MESSAGE, + sqlbuild_error=failure, ) - return None + self._publish(publications) + finished: FinishRow = session.finish() outcomes, schema_additions, analysis_names, contracts = finished self._record_catalog_changes( schema_additions=schema_additions, analysis_names=analysis_names ) - self._deferrals[DEFERRAL_DYNAMIC_PIVOT] += sum( - kind == CONTRACT_DEFERRED for kind, _ in contracts - ) - for kind, count in self._deferrals.items(): - record_analysis_deferral(kind=kind, count=count) self._record_cache(session) return { request.model_input.model_file.file_path.stem: self._model_analysis( - index=index, request=request, outcome=outcome, contract=contract + request=request, outcome=outcome, contract=contract ) - for index, (request, outcome, (_, contract)) in enumerate( - zip(self._python.requests, outcomes, contracts, strict=True) + for request, outcome, (_, contract) in zip( + self._python.requests, outcomes, contracts, strict=True ) } @@ -124,25 +103,6 @@ def _record_cache(self, session: _native.NativeModelAnalysisSession) -> None: return self._python.record_cached(hits=hits, misses=misses) - def _finished(self, session: _native.NativeModelAnalysisSession) -> FinishRow | None: - step: StepRow | None = session.run() - while step is not None: - publications, deferrals, failures = step - for failure in failures: - log_debug_event( - logger=_DEBUG_LOGGER, - message=NATIVE_ANALYSIS_FAILURE_MESSAGE, - sqlbuild_error=failure, - ) - self._publish(publications) - if not deferrals: - return session.finish() - answers: list[DeferredRow] = [self._answer(deferral) for deferral in deferrals] - if not session.provide(answers): - return None - step = session.run() - return None - def _publish(self, publications: ShapeRows) -> None: for name, shape in publications: published: dict[str, str] = dict(shape) @@ -151,29 +111,6 @@ def _publish(self, publications: ShapeRows) -> None: name, dict.fromkeys(published, InferredNullability.UNKNOWN) ) - def _answer(self, deferral: DeferralRow) -> DeferredRow: - kind, model, cleaned_sql, schemas, diagnostics, lineage = deferral - shapes: dict[str, dict[str, str]] = {name: dict(shape) for name, shape in schemas} - analysis: PolyglotAnalysisResult - if kind == DEFERRAL_ANALYSIS: - self._deferrals[DEFERRAL_LEGACY_ANALYSIS] += 1 - analysis = self._python.analyze_deferred( - model=model, - precomputed=PythonModelAnalysis.legacy_precomputed( - cleaned_sql=cleaned_sql or "", - binding_diagnostics=binding_diagnostics(diagnostics), - lineage=lineage, - ), - binding_schema=shapes, - column_types_by_table=self._types, - column_nullability_by_table=self._nullability, - ) - else: - self._deferrals[DEFERRAL_ENRICHMENT] += 1 - analysis = self._python.enrich(model=model, input_schemas=shapes) - self._kept[(model, kind)] = analysis - return deferred_row(model=model, analysis=analysis) - def _record_catalog_changes( self, *, schema_additions: ShapeRows, analysis_names: list[str] ) -> None: @@ -216,7 +153,6 @@ def _inferred_column(self, row: ColumnRow) -> InferredColumn: def _model_analysis( self, *, - index: int, request: ModelSqlAnalysisRequest, outcome: OutcomeRow, contract: ProofRow | None, @@ -236,8 +172,6 @@ def _model_analysis( (compact_lineage(lineage_rows) if succeeded else ()) if lineage_source == LINEAGE_NATIVE else lineage_facts(lineage_rows) - if lineage_source == LINEAGE_FACTS - else self._kept[(index, lineage_source)].lineage_columns ) return ModelSqlAnalysis( polyglot_analysis=PolyglotAnalysisResult( diff --git a/src/sqlbuild/compiler/analysis_session/constants.py b/src/sqlbuild/compiler/analysis_session/constants.py index 72600f5339..5685c3adf9 100644 --- a/src/sqlbuild/compiler/analysis_session/constants.py +++ b/src/sqlbuild/compiler/analysis_session/constants.py @@ -1,20 +1,11 @@ """Native model analysis session constants.""" ANALYSIS_DEFERRAL_SITE: str = "analysis_session" -DEFERRAL_NO_COMPACT_ANALYSIS: str = "no_compact_analysis" DEFERRAL_NO_CATALOG: str = "no_analysis_catalog" -DEFERRAL_SESSION: str = "session" -DEFERRAL_LEGACY_ANALYSIS: str = "legacy_analysis" -DEFERRAL_ENRICHMENT: str = "input_enrichment" -DEFERRAL_DYNAMIC_PIVOT: str = "dynamic_pivot" DEFERRAL_EXPRESSION_SHAPES: str = "expression_shapes" -DEFERRAL_ANALYSIS: str = "analysis" -DEFERRAL_ENRICH: str = "enrichment" LINEAGE_NATIVE: str = "native" LINEAGE_FACTS: str = "facts" -CONTRACT_DEFERRED: str = "deferred" NATIVE_ANALYSIS_FAILURE_MESSAGE: str = "native compact query analysis failed" -NATIVE_SESSION_FAILURE_MESSAGE: str = "native model analysis failed; falling back" NATIVE_SHAPES_FAILURE_MESSAGE: str = "native expression-source shape inference failed; falling back" NULLABILITY_RULE_FIRST_ARG: str = "first_arg" NULLABILITY_RULE_CONDITIONAL_RESULT: str = "conditional_result" diff --git a/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py b/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py index 8f72279583..1e808be7bd 100644 --- a/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py +++ b/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py @@ -9,7 +9,7 @@ ) -def analyze_native_model_sql(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses | None: - """Return each model's analysis (and pivot proof) and the session, or None to defer.""" +def analyze_native_model_sql(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses: + """Return each model's analysis (and pivot proof) and the session.""" return native_model_analyses(request=request) diff --git a/src/sqlbuild/compiler/analysis_session/types.py b/src/sqlbuild/compiler/analysis_session/types.py index 8fcf150865..b06a9f991e 100644 --- a/src/sqlbuild/compiler/analysis_session/types.py +++ b/src/sqlbuild/compiler/analysis_session/types.py @@ -6,11 +6,7 @@ type ColumnRow = tuple[str, str | None, str] type DiagnosticRow = tuple[str, str, int | None, int | None, int | None, int | None, str] type LineageItem = tuple[str, int, int, list[tuple[str, str, str]]] -type DeferredRow = tuple[int, bool, list[ColumnRow] | None, bool, bool, list[DiagnosticRow], bool] -type DeferralRow = tuple[ - str, int, str | None, ShapeRows, list[DiagnosticRow], list[LineageItem] | None -] -type StepRow = tuple[ShapeRows, list[DeferralRow], list[str]] +type StepRow = tuple[ShapeRows, list[str]] type OutcomeRow = tuple[ bool, list[ColumnRow] | None, diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/cache.py b/src/sqlbuild/compiler/compile/_helpers/analysis/cache.py index 10a92d3e45..2ae479e965 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/cache.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/cache.py @@ -1,109 +1,22 @@ -"""Versioned project-local cache for deterministic model SQL analysis facts.""" +"""The analysis cache context and the model analysis cache metrics.""" from __future__ import annotations import hashlib -import hmac import inspect import json import platform -import sqlite3 -import time -from collections import defaultdict, deque -from collections.abc import Sequence from importlib.metadata import PackageNotFoundError, version from pathlib import Path -from string import hexdigits -from typing import Any, cast - -import orjson from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.columns import table_function_analysis_name -from sqlbuild.compiler.compile.exceptions import AnalysisCacheEntryError -from sqlbuild.compiler.compile.models import ( - AnalysisCacheContext, - CompactAnalysisCacheCandidate, - CompactAnalysisCacheModel, - CompactAnalysisCachePlan, - CompactBatchPreparation, - CompactLineageFacts, - CompiledLineageColumnFact, - CompiledLineageSourceFact, - CompileSqlReference, - InferredColumn, - PolyglotAnalysisResult, -) -from sqlbuild.compiler.compile.types import CompactBatchResponseCallback, CompiledResourceType -from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, - InferredNullability, -) +from sqlbuild.compiler.compile.models import AnalysisCacheContext from sqlbuild.compiler.profiling.main._metric import record_compile_metric -from sqlbuild.compiler.profiling.main.record import record_compile_timing -from sqlbuild.compiler.references.types import SqlReferenceKind -from sqlbuild.compiler.sql_analysis.constants import BINDING_SEVERITIES, TYPE_CHECKED_DIALECTS -from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic +from sqlbuild.compiler.sql_analysis.constants import TYPE_CHECKED_DIALECTS _ANALYSIS_CACHE_VERSION: int = 13 _ANALYSIS_ALGORITHM_FINGERPRINT: str = "model-sql-analysis-v21-completed-star-shapes" -_LINEAGE_COLUMN_VALUE_COUNT: int = 4 -_LINEAGE_SOURCE_VALUE_COUNT: int = 3 -_COMPACT_TRANSFORM_CODES: dict[str, int] = { - CompactLineageFacts.transform_kind(code).value: code for code in range(6) -} -_COMPACT_CONFIDENCE_CODES: dict[str, int] = { - CompactLineageFacts.confidence(code).value: code for code in range(3) -} -_COMPACT_TRANSFORM_VALUES: tuple[str, ...] = tuple(_COMPACT_TRANSFORM_CODES) -_COMPACT_CONFIDENCE_VALUES: tuple[str, ...] = tuple(_COMPACT_CONFIDENCE_CODES) -_RESOURCE_TYPE_VALUES: frozenset[str] = frozenset(item.value for item in CompiledResourceType) -_NULLABILITY_BY_VALUE: dict[str, InferredNullability] = { - item.value: item for item in InferredNullability -} -_MAX_CACHE_ENTRY_BYTES: int = 10_000_000 -_SHA256_HEX_LENGTH: int = 64 -_CACHE_ENTRY_SEPARATOR: str = "\n" _LOCAL_QUALNAME_MARKER: str = "" -_SQLITE_QUERY_CHUNK_SIZE: int = 500 -_SQLITE_TIMEOUT_SECONDS: float = 0.1 -_CACHE_DATABASE_NAME: str = "model-analysis.sqlite3" -_COMPACT_BATCH_CACHE_VERSION: int = 1 -_MAX_COMPACT_BATCH_BYTES: int = 128 * 1024 * 1024 -_MAX_COMPACT_BATCH_RECORDS: int = 2 -_CREATE_CACHE_TABLE_SQL: str = """ -CREATE TABLE IF NOT EXISTS model_analysis ( - cache_key TEXT PRIMARY KEY, - payload TEXT NOT NULL -) -""" -_CREATE_SIGNATURE_TABLE_SQL: str = """ -CREATE TABLE IF NOT EXISTS model_analysis_signature ( - shared_fingerprint TEXT NOT NULL, - signature_namespace TEXT NOT NULL, - model_name TEXT NOT NULL, - output_signature TEXT NOT NULL, - PRIMARY KEY (shared_fingerprint, signature_namespace, model_name) -) -""" -_CREATE_DEPENDENCY_TABLE_SQL: str = """ -CREATE TABLE IF NOT EXISTS model_analysis_dependency ( - signature_namespace TEXT NOT NULL, - cache_key TEXT NOT NULL, - upstream_model_name TEXT NOT NULL, - output_signature TEXT NOT NULL, - PRIMARY KEY (signature_namespace, cache_key, upstream_model_name) -) -""" -_CREATE_COMPACT_BATCH_TABLE_SQL: str = """ -CREATE TABLE IF NOT EXISTS model_analysis_compact_batch ( - batch_key TEXT PRIMARY KEY, - shared_fingerprint TEXT NOT NULL, - payload BLOB NOT NULL, - created_ns INTEGER NOT NULL -) -""" def build_analysis_cache_context( @@ -142,186 +55,6 @@ def build_analysis_cache_context( return None -def model_analysis_cache_key( - *, - context: AnalysisCacheContext, - query_sql: str, - references: tuple[CompileSqlReference, ...], - placeholders: dict[str, str] | None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - binding_schema: dict[str, dict[str, str]] | None = None, - recover_cte_facts: bool = False, -) -> str: - """Return the exact analysis identity for one expanded model query.""" - - payload: dict[str, object] = { - "shared_fingerprint": context.shared_fingerprint, - "query_sql": query_sql, - "references": [ - { - "kind": str(reference.ref_kind), - "name": reference.ref_name, - "package": reference.ref_package, - "call_argument_count": reference.call_argument_count, - } - for reference in references - ], - "placeholders": dict(sorted((placeholders or {}).items())), - "referenced_column_nullability": _referenced_nullability_payload( - references=references, - column_nullability_by_table=column_nullability_by_table, - ), - "referenced_column_types": _referenced_types_payload( - references=references, - column_types_by_table=column_types_by_table, - ), - "binding_schema": binding_schema, - } - if recover_cte_facts: - payload["recover_cte_facts"] = True - return _json_value_digest(payload) - - -def compact_analysis_batch_cache_key( - *, context: AnalysisCacheContext, reuse_keys: tuple[str, ...] -) -> str: - """Return the exact identity of one ordered whole-project compact analysis batch.""" - - return _payload_digest( - { - "cache_version": _ANALYSIS_CACHE_VERSION, - "compact_batch_version": _COMPACT_BATCH_CACHE_VERSION, - "shared_fingerprint": context.shared_fingerprint, - "reuse_keys": reuse_keys, - } - ) - - -def compact_analysis_model_reuse_key( - *, cache_key: str, upstream_reuse_keys: tuple[tuple[str, str], ...] -) -> str: - """Return one model key that changes with every transitive model dependency.""" - - return _json_value_digest( - { - "cache_key": cache_key, - "upstream": upstream_reuse_keys, - } - ) - - -def build_compact_analysis_cache_plan( - *, - context: AnalysisCacheContext | None, - models: tuple[CompactAnalysisCacheModel, ...], - min_model_count: int, -) -> CompactAnalysisCachePlan | None: - """Build a dependency-aware ordered batch identity when every model is cacheable.""" - - if context is None or len(models) < min_model_count: - return None - models_by_name: dict[str, CompactAnalysisCacheModel] = {model.name: model for model in models} - dependencies_by_name: dict[str, tuple[str, ...]] = {} - for model in models: - dependencies_by_name[model.name] = tuple( - name for name in model.upstream_names if name in models_by_name - ) - dependents_by_name: dict[str, list[str]] = defaultdict(list) - remaining_dependency_count: dict[str, int] = {} - for model_name, dependencies in dependencies_by_name.items(): - remaining_dependency_count[model_name] = len(dependencies) - for dependency in dependencies: - dependents_by_name[dependency].append(model_name) - resolved: dict[str, str] = {} - ready: deque[str] = deque( - sorted(name for name, count in remaining_dependency_count.items() if count == 0) - ) - while ready: - model_name: str = ready.popleft() - model: CompactAnalysisCacheModel = models_by_name[model_name] - if model.cache_key is None: - return None - resolved[model_name] = compact_analysis_model_reuse_key( - cache_key=model.cache_key, - upstream_reuse_keys=tuple( - (upstream_name, resolved[upstream_name]) - for upstream_name in dependencies_by_name[model_name] - ), - ) - for dependent_name in sorted(dependents_by_name.get(model_name, ())): - remaining_dependency_count[dependent_name] -= 1 - if remaining_dependency_count[dependent_name] == 0: - ready.append(dependent_name) - if len(resolved) != len(models): - return None - cache_keys: tuple[str, ...] = tuple( - model.cache_key for model in models if model.cache_key is not None - ) - if len(cache_keys) != len(models): - return None - reuse_keys: tuple[str, ...] = tuple(resolved[model.name] for model in models) - return CompactAnalysisCachePlan( - context=context, - batch_key=compact_analysis_batch_cache_key(context=context, reuse_keys=reuse_keys), - cache_keys=cache_keys, - reuse_keys=reuse_keys, - ) - - -def read_compact_analysis_cache_candidate( - *, plan: CompactAnalysisCachePlan, expected_count: int, min_model_count: int -) -> CompactAnalysisCacheCandidate | None: - """Read one compatible generation and locate dependency-safe matching positions.""" - - cached: tuple[tuple[str, ...], tuple[str, ...], tuple[str, ...], object] | None = ( - read_compact_analysis_batch(context=plan.context, batch_key=plan.batch_key) - ) - if cached is None: - return None - stored_cache_keys, stored_reuse_keys, cleaned_sql, response = cached - matching_indexes: tuple[int, ...] = tuple( - index - for index, (stored_key, current_key) in enumerate( - zip(stored_reuse_keys, plan.reuse_keys, strict=False) - ) - if stored_key == current_key - ) - if len(stored_cache_keys) != expected_count or len(matching_indexes) < min_model_count: - return None - return CompactAnalysisCacheCandidate( - matching_indexes=matching_indexes, - preparation=CompactBatchPreparation( - cleaned_sql=cleaned_sql, - queries=(), - templates=(), - projections=(), - ), - response=response, - ) - - -def compact_analysis_batch_response_writer( - *, plan: CompactAnalysisCachePlan, count: int -) -> CompactBatchResponseCallback: - """Build a callback that publishes one complete compact generation.""" - - def write(*, preparation: CompactBatchPreparation, response: object) -> None: - if len(preparation.cleaned_sql) != count: - return - with record_compile_timing("cache_publication_ms"): - write_compact_analysis_batch( - context=plan.context, - batch_key=plan.batch_key, - cache_keys=plan.cache_keys, - reuse_keys=plan.reuse_keys, - cleaned_sql=preparation.cleaned_sql, - response=response, - ) - - return write - - def record_analysis_cache_metrics( *, batch_hits: int, entry_hits: int, misses: int, bypasses: int ) -> None: @@ -333,399 +66,6 @@ def record_analysis_cache_metrics( record_compile_metric(metric="analysis_cache_bypasses", value=bypasses) -def read_compact_analysis_batch( - *, context: AnalysisCacheContext, batch_key: str -) -> tuple[tuple[str, ...], tuple[str, ...], tuple[str, ...], object] | None: - """Read the newest compatible compact generation, treating every fault as a miss.""" - - database_path: Path = _cache_database_path(context=context) - if not database_path.is_file(): - return None - try: - connection_uri: str = f"file:{database_path}?mode=ro" - with sqlite3.connect( - connection_uri, - uri=True, - timeout=_SQLITE_TIMEOUT_SECONDS, - ) as connection: - metadata_row: tuple[str, int] | None = connection.execute( - "SELECT batch_key, length(payload) FROM model_analysis_compact_batch " - "WHERE batch_key = ?", - (batch_key,), - ).fetchone() - if metadata_row is None: - metadata_row = connection.execute( - "SELECT batch_key, length(payload) FROM model_analysis_compact_batch " - "WHERE shared_fingerprint = ? ORDER BY created_ns DESC LIMIT 1", - (context.shared_fingerprint,), - ).fetchone() - if metadata_row is None: - return None - stored_batch_key, payload_bytes = metadata_row - if payload_bytes < 0 or payload_bytes > _MAX_COMPACT_BATCH_BYTES: - return None - payload_row: tuple[bytes] | None = connection.execute( - "SELECT payload FROM model_analysis_compact_batch WHERE batch_key = ?", - (stored_batch_key,), - ).fetchone() - if payload_row is None: - return None - contents: bytes = payload_row[0] - if not isinstance(contents, bytes) or len(contents) != payload_bytes: - return None - envelope: object = orjson.loads(contents) - if not isinstance(envelope, dict): - return None - values: dict[str, Any] = cast(dict[str, Any], envelope) - cache_keys_value: object = values.get("cache_keys") - reuse_keys_value: object = values.get("reuse_keys") - cleaned_sql_value: object = values.get("cleaned_sql") - response: object = values.get("response") - if ( - not isinstance(cache_keys_value, list) - or not all(isinstance(value, str) for value in cache_keys_value) - or not isinstance(reuse_keys_value, list) - or not all(isinstance(value, str) for value in reuse_keys_value) - or not isinstance(cleaned_sql_value, list) - or not all(isinstance(value, str) for value in cleaned_sql_value) - ): - return None - cache_keys: tuple[str, ...] = tuple(cache_keys_value) - reuse_keys: tuple[str, ...] = tuple(reuse_keys_value) - cleaned_sql: tuple[str, ...] = tuple(cleaned_sql_value) - if ( - values.get("version") != _COMPACT_BATCH_CACHE_VERSION - or values.get("batch_key") != stored_batch_key - or values.get("shared_fingerprint") != context.shared_fingerprint - or values.get("count") != len(cache_keys) - or len(reuse_keys) != len(cache_keys) - or len(cleaned_sql) != len(cache_keys) - or not isinstance(values.get("facts_sha256"), str) - or not hmac.compare_digest( - values["facts_sha256"], - _compact_batch_facts_digest( - cache_keys=cache_keys, - reuse_keys=reuse_keys, - cleaned_sql=cleaned_sql, - response=response, - ), - ) - ): - return None - return cache_keys, reuse_keys, cleaned_sql, response - except (OSError, sqlite3.DatabaseError, TypeError, ValueError, orjson.JSONDecodeError): - return None - - -def write_compact_analysis_batch( - *, - context: AnalysisCacheContext, - batch_key: str, - cache_keys: tuple[str, ...], - reuse_keys: tuple[str, ...], - cleaned_sql: tuple[str, ...], - response: object, -) -> None: - """Persist one compact native response transactionally, with bounded logical retention.""" - - try: - if not (len(cache_keys) == len(reuse_keys) == len(cleaned_sql)): - return - contents: bytes = orjson.dumps( - { - "version": _COMPACT_BATCH_CACHE_VERSION, - "batch_key": batch_key, - "shared_fingerprint": context.shared_fingerprint, - "count": len(cleaned_sql), - "facts_sha256": _compact_batch_facts_digest( - cache_keys=cache_keys, - reuse_keys=reuse_keys, - cleaned_sql=cleaned_sql, - response=response, - ), - "cache_keys": cache_keys, - "reuse_keys": reuse_keys, - "cleaned_sql": cleaned_sql, - "response": response, - }, - option=orjson.OPT_SORT_KEYS, - ) - if len(contents) > _MAX_COMPACT_BATCH_BYTES: - return - database_path: Path = _cache_database_path(context=context) - database_path.parent.mkdir(parents=True, exist_ok=True) - with sqlite3.connect(database_path, timeout=_SQLITE_TIMEOUT_SECONDS) as connection: - _ = connection.execute(_CREATE_COMPACT_BATCH_TABLE_SQL) - _ = connection.execute( - "INSERT OR REPLACE INTO model_analysis_compact_batch " - "(batch_key, shared_fingerprint, payload, created_ns) VALUES (?, ?, ?, ?)", - (batch_key, context.shared_fingerprint, contents, time.time_ns()), - ) - _ = connection.execute( - "DELETE FROM model_analysis_compact_batch WHERE batch_key NOT IN " - "(SELECT batch_key FROM model_analysis_compact_batch " - "ORDER BY created_ns DESC LIMIT ?)", - (_MAX_COMPACT_BATCH_RECORDS,), - ) - except (OSError, sqlite3.DatabaseError, TypeError, ValueError): - return - - -def _compact_batch_facts_digest( - *, - cache_keys: tuple[str, ...], - reuse_keys: tuple[str, ...], - cleaned_sql: tuple[str, ...], - response: object, -) -> str: - digest: Any = hashlib.sha256() - for label, value in ( - (b"cache_keys\0", cache_keys), - (b"reuse_keys\0", reuse_keys), - (b"cleaned_sql\0", cleaned_sql), - (b"response\0", response), - ): - digest.update(label) - digest.update(orjson.dumps(value, option=orjson.OPT_SORT_KEYS)) - digest.update(b"\0") - return digest.hexdigest() - - -def read_model_analyses( - *, - context: AnalysisCacheContext, - cache_keys: tuple[str, ...], - model_names: tuple[str, ...], - upstream_model_names_by_key: dict[str, tuple[str, ...]], -) -> tuple[dict[str, PolyglotAnalysisResult], dict[str, str], dict[str, str]]: - """Read one consistent snapshot of analyses, dependencies, and output signatures.""" - - database_path: Path = _cache_database_path(context=context) - if not database_path.is_file() or not cache_keys: - return {}, {}, {} - analyses: dict[str, PolyglotAnalysisResult] = {} - signatures: dict[str, str] = {} - output_signatures_by_key: dict[str, str] = {} - sources: _LineageSourceInterner = _LineageSourceInterner() - try: - connection_uri: str = f"file:{database_path}?mode=ro" - with sqlite3.connect( - connection_uri, - uri=True, - timeout=_SQLITE_TIMEOUT_SECONDS, - ) as connection: - _ = connection.execute("BEGIN") - signature_model_name_set: set[str] = set(model_names) - upstream_names: tuple[str, ...] - for upstream_names in upstream_model_names_by_key.values(): - signature_model_name_set.update(upstream_names) - signature_model_names: tuple[str, ...] = tuple(sorted(signature_model_name_set)) - for start in range(0, len(signature_model_names), _SQLITE_QUERY_CHUNK_SIZE): - model_chunk: tuple[str, ...] = signature_model_names[ - start : start + _SQLITE_QUERY_CHUNK_SIZE - ] - model_placeholders: str = ",".join("?" for _ in model_chunk) - signature_rows: list[tuple[str, str]] = connection.execute( - f"SELECT model_name, output_signature FROM model_analysis_signature " - f"WHERE shared_fingerprint = ? AND signature_namespace = ? " - f"AND model_name IN ({model_placeholders})", - ( - context.shared_fingerprint, - context.signature_namespace, - *model_chunk, - ), - ).fetchall() - signatures.update(signature_rows) - for start in range(0, len(cache_keys), _SQLITE_QUERY_CHUNK_SIZE): - chunk: tuple[str, ...] = cache_keys[start : start + _SQLITE_QUERY_CHUNK_SIZE] - placeholders: str = ",".join("?" for _ in chunk) - rows: list[tuple[str, str]] = connection.execute( - f"SELECT cache_key, payload FROM model_analysis " - f"WHERE cache_key IN ({placeholders})", - chunk, - ).fetchall() - dependency_rows: list[tuple[str, str, str]] = connection.execute( - f"SELECT cache_key, upstream_model_name, output_signature " - f"FROM model_analysis_dependency WHERE signature_namespace = ? " - f"AND cache_key IN ({placeholders})", - (context.signature_namespace, *chunk), - ).fetchall() - dependencies_by_key: dict[str, dict[str, str]] = {} - for cache_key, upstream_name, output_signature in dependency_rows: - dependencies_by_key.setdefault(cache_key, {})[upstream_name] = output_signature - for cache_key, contents in rows: - expected_dependencies: dict[str, str] | None = _expected_dependencies( - upstream_model_names=upstream_model_names_by_key.get(cache_key, ()), - signatures=signatures, - ) - if ( - expected_dependencies is None - or dependencies_by_key.get(cache_key, {}) != expected_dependencies - ): - continue - cached_result: tuple[PolyglotAnalysisResult, str] | None = ( - _analysis_from_contents( - contents=contents, - expected_cache_key=cache_key, - sources=sources, - ) - ) - if cached_result is not None: - analysis, output_signature = cached_result - analyses[cache_key] = analysis - output_signatures_by_key[cache_key] = output_signature - except (OSError, sqlite3.DatabaseError): - return {}, {}, {} - return analyses, signatures, output_signatures_by_key - - -def write_model_analyses( - *, - context: AnalysisCacheContext, - analyses_by_key: dict[str, PolyglotAnalysisResult], - latest_analyses_by_model: dict[str, PolyglotAnalysisResult] | None = None, - dependency_signatures_by_key: dict[str, dict[str, str]] | None = None, -) -> None: - """Transactionally persist deterministic analyses; cache failures never fail compilation.""" - - signed_analyses: dict[int, PolyglotAnalysisResult] = { - id(analysis): analysis - for analysis in (*analyses_by_key.values(), *(latest_analyses_by_model or {}).values()) - } - output_signatures: dict[int, str] = { - key: model_analysis_output_signature(analysis) for key, analysis in signed_analyses.items() - } - rows: list[tuple[str, str]] = [] - for cache_key, analysis in analyses_by_key.items(): - try: - contents: str = _analysis_contents( - cache_key=cache_key, - analysis=analysis, - output_signature=output_signatures[id(analysis)], - ) - except orjson.JSONEncodeError: - continue - if len(contents.encode()) <= _MAX_CACHE_ENTRY_BYTES: - rows.append((cache_key, contents)) - signature_rows: list[tuple[str, str, str, str]] = [ - ( - context.shared_fingerprint, - context.signature_namespace, - model_name, - output_signatures[id(analysis)], - ) - for model_name, analysis in (latest_analyses_by_model or {}).items() - ] - dependency_rows: list[tuple[str, str, str, str]] = [] - for cache_key, dependencies in (dependency_signatures_by_key or {}).items(): - analysis: PolyglotAnalysisResult | None = analyses_by_key.get(cache_key) - if analysis is None: - continue - dependency_rows.extend( - (context.signature_namespace, cache_key, upstream_name, output_signature) - for upstream_name, output_signature in dependencies.items() - ) - if not rows and not signature_rows: - return - try: - database_path: Path = _cache_database_path(context=context) - database_path.parent.mkdir(parents=True, exist_ok=True) - with sqlite3.connect(database_path, timeout=_SQLITE_TIMEOUT_SECONDS) as connection: - _ = connection.execute(_CREATE_CACHE_TABLE_SQL) - _ = connection.execute(_CREATE_SIGNATURE_TABLE_SQL) - _ = connection.execute(_CREATE_DEPENDENCY_TABLE_SQL) - _ = connection.executemany( - "INSERT OR REPLACE INTO model_analysis (cache_key, payload) VALUES (?, ?)", - rows, - ) - written_cache_keys: tuple[str, ...] = tuple(cache_key for cache_key, _ in rows) - for start in range(0, len(written_cache_keys), _SQLITE_QUERY_CHUNK_SIZE): - chunk: tuple[str, ...] = written_cache_keys[ - start : start + _SQLITE_QUERY_CHUNK_SIZE - ] - placeholders: str = ",".join("?" for _ in chunk) - _ = connection.execute( - f"DELETE FROM model_analysis_dependency " - f"WHERE signature_namespace = ? AND cache_key IN ({placeholders})", - (context.signature_namespace, *chunk), - ) - _ = connection.executemany( - "INSERT INTO model_analysis_dependency " - "(signature_namespace, cache_key, upstream_model_name, output_signature) " - "VALUES (?, ?, ?, ?)", - dependency_rows, - ) - _ = connection.executemany( - "INSERT OR REPLACE INTO model_analysis_signature " - "(shared_fingerprint, signature_namespace, model_name, output_signature) " - "VALUES (?, ?, ?, ?)", - signature_rows, - ) - except (OSError, sqlite3.DatabaseError): - return - - -def _expected_dependencies( - *, - upstream_model_names: tuple[str, ...], - signatures: dict[str, str], -) -> dict[str, str] | None: - if any(name not in signatures for name in upstream_model_names): - return None - return {name: signatures[name] for name in upstream_model_names} - - -def model_analysis_output_signature(analysis: PolyglotAnalysisResult) -> str: - """Return the exported column signature relevant to downstream analysis.""" - - return _json_value_digest( - { - "columns": ( - None - if analysis.columns is None - else [ - { - "name": column.name, - "type": column.type, - "nullability": column.nullability.value, - } - for column in analysis.columns - ] - ), - "has_star": analysis.has_star, - "star_resolved": analysis.star_resolved, - } - ) - - -def _analysis_from_contents( - *, - contents: object, - expected_cache_key: str, - sources: _LineageSourceInterner | None = None, -) -> tuple[PolyglotAnalysisResult, str] | None: - if not isinstance(contents, str): - return None - try: - if len(contents.encode()) > _MAX_CACHE_ENTRY_BYTES: - return None - stored_digest, separator, serialized_payload = contents.partition(_CACHE_ENTRY_SEPARATOR) - if not separator or not hmac.compare_digest( - stored_digest, - _cache_entry_digest( - cache_key=expected_cache_key, - serialized_payload=serialized_payload, - ), - ): - return None - payload: object = orjson.loads(serialized_payload) - return _analysis_from_payload( - payload=payload, expected_cache_key=expected_cache_key, sources=sources - ) - except (ValueError, TypeError, KeyError, RecursionError, orjson.JSONDecodeError): - return None - - def _inference_profile_payload( profile: ExpressionInferenceProfile, ) -> dict[str, object] | None: @@ -765,372 +105,6 @@ def _inference_profile_payload( } -def _referenced_nullability_payload( - *, - references: tuple[CompileSqlReference, ...], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> dict[str, dict[str, str]]: - payload: dict[str, dict[str, str]] = {} - table: str - for table in _referenced_analysis_tables(references): - columns: dict[str, InferredNullability] | None = column_nullability_by_table.get(table) - if columns is None: - continue - payload[table] = { - column: nullability.value for column, nullability in sorted(columns.items()) - } - return payload - - -def _referenced_types_payload( - *, - references: tuple[CompileSqlReference, ...], - column_types_by_table: dict[str, dict[str, str]], -) -> dict[str, dict[str, str]]: - payload: dict[str, dict[str, str]] = {} - table: str - for table in _referenced_analysis_tables(references): - columns: dict[str, str] | None = column_types_by_table.get(table) - if columns is not None: - payload[table] = dict(sorted(columns.items())) - return payload - - -def _referenced_analysis_tables( - references: tuple[CompileSqlReference, ...], -) -> tuple[str, ...]: - names: set[str] = set() - for reference in references: - names.add( - table_function_analysis_name(reference.ref_name) - if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION - else reference.ref_name - ) - return tuple(sorted(names)) - - -def _analysis_payload( - *, cache_key: str, analysis: PolyglotAnalysisResult, output_signature: str | None = None -) -> dict[str, object]: - return { - "v": _ANALYSIS_CACHE_VERSION, - "k": cache_key, - "a": analysis.analysis_succeeded, - "s": ( - model_analysis_output_signature(analysis) - if output_signature is None - else output_signature - ), - "c": ( - None - if analysis.columns is None - else [ - [column.name, column.type, column.nullability.value] for column in analysis.columns - ] - ), - "l": _lineage_payload(analysis.lineage_columns), - "h": analysis.has_star, - "sr": analysis.star_resolved, - "b": [ - [ - diagnostic.code, - diagnostic.message, - diagnostic.line, - diagnostic.column, - diagnostic.start, - diagnostic.end, - diagnostic.severity, - ] - for diagnostic in analysis.binding_diagnostics - ], - "bv": analysis.binding_validated, - } - - -def _lineage_payload(lineage_columns: Sequence[CompiledLineageColumnFact]) -> list[list[object]]: - if isinstance(lineage_columns, CompactLineageFacts): - compact: list[list[object]] | None = _compact_lineage_payload(lineage_columns) - if compact is not None: - return compact - return [_lineage_column_payload(column) for column in lineage_columns] - - -def _compact_lineage_payload(lineage: CompactLineageFacts) -> list[list[object]] | None: - """Serialise indexed rows without projecting facts; None defers to the validating projection.""" - - pool: tuple[str, ...] = lineage.string_pool - payload: list[list[object]] = [] - for name_index, transform_code, confidence_code, sources in lineage.rows: - upstream_columns: list[list[str]] = [] - for resource_type_index, resource_name_index, column_index in sources: - resource_type: str = pool[resource_type_index] - if resource_type not in _RESOURCE_TYPE_VALUES: - return None - upstream_columns.append( - [resource_type, lineage.resource_name(resource_name_index), pool[column_index]] - ) - payload.append( - [ - pool[name_index], - _COMPACT_TRANSFORM_VALUES[transform_code], - _COMPACT_CONFIDENCE_VALUES[confidence_code], - upstream_columns, - ] - ) - return payload - - -def _lineage_column_payload(column: CompiledLineageColumnFact) -> list[object]: - return [ - column.output_column, - column.transform_kind.value, - column.confidence.value, - [ - [ - str(source.resource_type), - source.resource_name, - source.column_name, - ] - for source in column.upstream_columns - ], - ] - - -def _analysis_from_payload( - *, - payload: object, - expected_cache_key: str, - sources: _LineageSourceInterner | None = None, -) -> tuple[PolyglotAnalysisResult, str]: - if not isinstance(payload, dict): - raise AnalysisCacheEntryError("analysis cache entry must be an object") - values: dict[str, Any] = cast(dict[str, Any], payload) - version_value: object = values["v"] - if type(version_value) is not int or version_value != _ANALYSIS_CACHE_VERSION: - raise AnalysisCacheEntryError("analysis cache version mismatch") - if values["k"] != expected_cache_key: - raise AnalysisCacheEntryError("analysis cache key mismatch") - analysis_succeeded: object = values["a"] - if not isinstance(analysis_succeeded, bool): - raise AnalysisCacheEntryError("analysis cache success flag must be a boolean") - output_signature: object = values["s"] - if not ( - isinstance(output_signature, str) - and len(output_signature) == _SHA256_HEX_LENGTH - and all(character in hexdigits for character in output_signature) - ): - raise AnalysisCacheEntryError("analysis cache output signature is invalid") - columns_payload: object = values["c"] - columns: tuple[InferredColumn, ...] | None = ( - None - if columns_payload is None - else tuple(_column_from_payload(column) for column in _value_lists(columns_payload)) - ) - lineage_payload: list[list[object]] = _value_lists(values["l"]) - lineage_columns: Sequence[CompiledLineageColumnFact] | None = _compact_lineage_from_payload( - payload=lineage_payload - ) - if lineage_columns is None: - interner: _LineageSourceInterner = _LineageSourceInterner() if sources is None else sources - lineage_columns = tuple( - _lineage_column_from_payload(payload=column, sources=interner) - for column in lineage_payload - ) - has_star: object = values["h"] - if not isinstance(has_star, bool): - raise AnalysisCacheEntryError("analysis cache has_star must be a boolean") - binding_diagnostics: tuple[SqlBindingDiagnostic, ...] = tuple( - _binding_diagnostic_from_payload(value) for value in _value_lists(values["b"]) - ) - binding_validated: object = values["bv"] - if not isinstance(binding_validated, bool): - raise AnalysisCacheEntryError("analysis cache binding validation flag must be a boolean") - return ( - PolyglotAnalysisResult( - analysis_succeeded=analysis_succeeded, - columns=columns, - lineage_columns=lineage_columns, - has_star=has_star, - star_resolved=values.get("sr") is True, - binding_diagnostics=binding_diagnostics, - binding_validated=binding_validated, - ), - output_signature, - ) - - -def _binding_diagnostic_from_payload(payload: list[object]) -> SqlBindingDiagnostic: - diagnostic_value_count: int = 7 - if len(payload) != diagnostic_value_count: - raise AnalysisCacheEntryError("analysis cache binding diagnostic must contain seven values") - code, message, line, column, start, end, severity = payload - if severity not in BINDING_SEVERITIES: - raise AnalysisCacheEntryError("analysis cache binding diagnostic severity is invalid") - if not isinstance(code, str) or not isinstance(message, str): - raise AnalysisCacheEntryError( - "analysis cache binding diagnostic code/message must be strings" - ) - positions: tuple[object, ...] = (line, column, start, end) - if any(value is not None and not isinstance(value, int) for value in positions): - raise AnalysisCacheEntryError("analysis cache binding diagnostic positions are invalid") - return SqlBindingDiagnostic( - code=code, - message=message, - line=cast(int | None, line), - column=cast(int | None, column), - start=cast(int | None, start), - end=cast(int | None, end), - severity=cast(str, severity), - ) - - -def _value_lists(payload: object) -> list[list[object]]: - if not isinstance(payload, list) or not all(isinstance(item, list) for item in payload): - raise AnalysisCacheEntryError("analysis cache collection must contain arrays") - return cast(list[list[object]], payload) - - -def _column_from_payload(payload: list[object]) -> InferredColumn: - column_value_count: int = 3 - if len(payload) != column_value_count: - raise AnalysisCacheEntryError("analysis cache column must contain three values") - name, column_type, nullability = payload - if not isinstance(name, str) or not isinstance(nullability, str): - raise AnalysisCacheEntryError("analysis cache column name and nullability must be strings") - if column_type is not None and not isinstance(column_type, str): - raise AnalysisCacheEntryError("analysis cache column type must be a string or null") - return InferredColumn( - name=name, - type=column_type, - nullability=_NULLABILITY_BY_VALUE.get(nullability) or InferredNullability(nullability), - ) - - -def _compact_lineage_from_payload(*, payload: list[list[object]]) -> CompactLineageFacts | None: - """Validate cached lineage into lazy indexed rows; None means use the eager decoder.""" - - pool: dict[str, int] = {} - rows: list[tuple[int, int, int, tuple[tuple[int, int, int], ...]]] = [] - column: list[object] - for column in payload: - if len(column) != _LINEAGE_COLUMN_VALUE_COUNT: - raise AnalysisCacheEntryError("analysis cache lineage column must contain four values") - output_column, transform_kind, confidence, upstream_columns = column - if not ( - type(output_column) is str and type(transform_kind) is str and type(confidence) is str - ): - raise AnalysisCacheEntryError("analysis cache lineage attributes must be strings") - transform_code: int | None = _COMPACT_TRANSFORM_CODES.get(transform_kind) - confidence_code: int | None = _COMPACT_CONFIDENCE_CODES.get(confidence) - if transform_code is None or confidence_code is None: - return None - if type(upstream_columns) is not list: - raise AnalysisCacheEntryError("analysis cache collection must contain arrays") - source_rows: list[tuple[int, int, int]] = [] - source: object - for source in upstream_columns: - if not ( - type(source) is list - and len(source) == _LINEAGE_SOURCE_VALUE_COUNT - and type(source[0]) is str - and type(source[1]) is str - and type(source[2]) is str - ): - raise AnalysisCacheEntryError( - "analysis cache lineage source must contain three strings" - ) - if source[0] not in _RESOURCE_TYPE_VALUES: - raise AnalysisCacheEntryError("analysis cache lineage resource type is invalid") - source_rows.append( - ( - pool.setdefault(source[0], len(pool)), - pool.setdefault(source[1], len(pool)), - pool.setdefault(source[2], len(pool)), - ) - ) - rows.append( - ( - pool.setdefault(output_column, len(pool)), - transform_code, - confidence_code, - tuple(source_rows), - ) - ) - return CompactLineageFacts(string_pool=tuple(pool), rows=tuple(rows)) - - -class _LineageSourceInterner: - """Share identical immutable lineage source facts across one cache read.""" - - def __init__(self) -> None: - self._facts: dict[tuple[str, str, str], CompiledLineageSourceFact] = {} - - def decode(self, *, payload: list[object]) -> CompiledLineageSourceFact: - source_value_count: int = 3 - if len(payload) != source_value_count or not all( - isinstance(value, str) for value in payload - ): - raise AnalysisCacheEntryError( - "analysis cache lineage source must contain three strings" - ) - resource_type, resource_name, column_name = cast(list[str], payload) - key: tuple[str, str, str] = (resource_type, resource_name, column_name) - fact: CompiledLineageSourceFact | None = self._facts.get(key) - if fact is None: - fact = CompiledLineageSourceFact( - resource_type=resource_type, - resource_name=resource_name, - column_name=column_name, - ) - self._facts[key] = fact - return fact - - -def _lineage_column_from_payload( - *, payload: list[object], sources: _LineageSourceInterner -) -> CompiledLineageColumnFact: - lineage_value_count: int = 4 - if len(payload) != lineage_value_count: - raise AnalysisCacheEntryError("analysis cache lineage column must contain four values") - output_column, transform_kind, confidence, upstream_columns = payload - if not all(isinstance(value, str) for value in (output_column, transform_kind, confidence)): - raise AnalysisCacheEntryError("analysis cache lineage attributes must be strings") - return CompiledLineageColumnFact( - output_column=cast(str, output_column), - transform_kind=ColumnTransformKind(cast(str, transform_kind)), - confidence=ColumnLineageConfidence(cast(str, confidence)), - upstream_columns=tuple( - sources.decode(payload=source) for source in _value_lists(upstream_columns) - ), - ) - - -def _cache_database_path(*, context: AnalysisCacheContext) -> Path: - return context.root / f"v{_ANALYSIS_CACHE_VERSION}" / _CACHE_DATABASE_NAME - - -def _analysis_contents( - *, cache_key: str, analysis: PolyglotAnalysisResult, output_signature: str | None = None -) -> str: - serialized_payload: str = orjson.dumps( - _analysis_payload( - cache_key=cache_key, analysis=analysis, output_signature=output_signature - ), - option=orjson.OPT_SORT_KEYS, - ).decode() - return _CACHE_ENTRY_SEPARATOR.join( - ( - _cache_entry_digest(cache_key=cache_key, serialized_payload=serialized_payload), - serialized_payload, - ) - ) - - -def _cache_entry_digest(*, cache_key: str, serialized_payload: str) -> str: - encoded: bytes = f"{cache_key}\0{serialized_payload}".encode() - return hashlib.sha256(encoded).hexdigest() - - def _payload_digest(payload: object) -> str: encoded: bytes = json.dumps( payload, @@ -1141,12 +115,6 @@ def _payload_digest(payload: object) -> str: return hashlib.sha256(encoded).hexdigest() -def _json_value_digest(payload: dict[str, object]) -> str: - """Digest string-keyed JSON scalars exactly as _payload_digest does, via orjson.""" - - return hashlib.sha256(orjson.dumps(payload, option=orjson.OPT_SORT_KEYS)).hexdigest() - - def _package_version(package: str) -> str: try: return version(package) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/dynamic_pivot.py b/src/sqlbuild/compiler/compile/_helpers/analysis/dynamic_pivot.py deleted file mode 100644 index 5089e2b14d..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/dynamic_pivot.py +++ /dev/null @@ -1,728 +0,0 @@ -"""Compiler proof for closed contracts over runtime-dynamic pivot columns.""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any, cast - -from sqlbuild.compiler.compile.models import ( - DynamicColumnContractProof, - DynamicColumnFamilyProof, - InferredColumn, -) -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily - -_DUCKDB_DIALECT: str = "duckdb" -_MOTHERDUCK_DIALECT: str = "motherduck" -_SNOWFLAKE_DIALECT: str = "snowflake" -_PIVOT_AST_KIND: str = "pivot" -_SELECT_AST_KIND: str = "select" -_QUERY_AST_KINDS: frozenset[str] = frozenset({"query", "subquery"}) -_CAST_AST_KIND: str = "cast" -_COLUMN_AST_KIND: str = "column" -_TABLE_AST_KIND: str = "table" -_FUNCTION_AST_KIND: str = "function" -_ALIAS_AST_KIND: str = "alias" -_CTE_AST_KIND: str = "cte" -_STAR_AST_KIND: str = "star" -_DYNAMIC_VALUE_SOURCE_KINDS: frozenset[str] = frozenset({"pivot_any", _SELECT_AST_KIND, "query"}) -_DEPENDENCY_FUNCTION_NAMES: frozenset[str] = frozenset({"__ref", "__source", "__seed"}) -_UNKNOWN_DATA_TYPE: str = "UNKNOWN" -_SIMPLIFIED_PIVOT_DIALECTS: frozenset[str] = frozenset({_DUCKDB_DIALECT, _MOTHERDUCK_DIALECT}) -_SUPPORTED_DIALECTS: frozenset[str] = frozenset({*_SIMPLIFIED_PIVOT_DIALECTS, _SNOWFLAKE_DIALECT}) -_TYPE_PRESERVING_AGGREGATES: frozenset[str] = frozenset({"ANY_VALUE", "MAX", "MIN"}) -type _CteMap = dict[str, dict[str, object] | None] - - -@dataclass(frozen=True) -class _ColumnFact: - name: str - type: str | None - nullability: InferredNullability = InferredNullability.UNKNOWN - - -@dataclass(frozen=True) -class _Boundary: - pivot: dict[str, object] | None = None - passthrough_name: str | None = None - - -def analyze_dynamic_column_contract( - *, - query_sql: str, - dialect: str | None, - families: tuple[SchemaDynamicColumnFamily, ...], - column_types_by_table: dict[str, dict[str, str]], - authoritative_column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> DynamicColumnContractProof | None: - """Prove that a model output is closed over fixed columns and declared pivot families.""" - - if not families: - return None - normalized_dialect: str = (dialect or "").casefold() - if normalized_dialect not in _SUPPORTED_DIALECTS: - return _failure( - f"adapter dialect '{dialect or 'generic'}' does not support compiler-proven " - "dynamic pivots" - ) - parse_dialect: str = ( - _DUCKDB_DIALECT if normalized_dialect == _MOTHERDUCK_DIALECT else normalized_dialect - ) - polyglot: Any = import_polyglot_sql() - try: - parsed: Any = polyglot.parse_one(query_sql, dialect=parse_dialect) - except polyglot.PolyglotError as error: - return _failure(f"dynamic pivot SQL could not be parsed: {error}") - root: object = parsed.to_dict() - if not isinstance(root, dict): - return _failure("dynamic pivot SQL did not produce a structured query") - ctes: _CteMap = _collect_ctes(root) - boundary: _Boundary | None = _resolve_boundary(node=root, ctes=ctes, seen=frozenset()) - if boundary is None: - return _failure( - "output must be a wildcard projection from one supported dynamic pivot or a proven " - "dynamic-family passthrough" - ) - if boundary.passthrough_name is not None: - return _analyze_passthrough( - name=boundary.passthrough_name, - families=families, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - dynamic_families_by_table=dynamic_families_by_table, - ) - if boundary.pivot is None: - return _failure("dynamic output boundary did not resolve to a pivot") - return _analyze_pivot( - pivot=boundary.pivot, - dialect=normalized_dialect, - families=families, - ctes=ctes, - column_types_by_table=authoritative_column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - bare_dynamic_pivot=_node_key(root) == _PIVOT_AST_KIND, - ) - - -def _analyze_passthrough( - *, - name: str, - families: tuple[SchemaDynamicColumnFamily, ...], - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> DynamicColumnContractProof: - upstream_families: tuple[SchemaDynamicColumnFamily, ...] | None = _casefold_lookup( - mapping=dynamic_families_by_table, key=name - ) - if upstream_families is None or not _families_equal(left=families, right=upstream_families): - return _failure( - f"dynamic-family passthrough from '{name}' must redeclare the upstream families exactly" - ) - source_columns: tuple[_ColumnFact, ...] | None = _external_schema( - name=name, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - ) - if source_columns is None: - return _failure(f"dynamic-family passthrough source '{name}' has no authoritative schema") - return DynamicColumnContractProof( - output_proven=True, - fixed_columns=tuple( - InferredColumn(name=column.name, type=column.type, nullability=column.nullability) - for column in source_columns - ), - families=tuple( - DynamicColumnFamilyProof(name=family.name, inferred_type=family.type) - for family in families - ), - input_relations=(name,), - ) - - -def _analyze_pivot( - *, - pivot: dict[str, object], - dialect: str, - families: tuple[SchemaDynamicColumnFamily, ...], - ctes: _CteMap, - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - bare_dynamic_pivot: bool, -) -> DynamicColumnContractProof: - if bool(pivot.get("unpivot")): - return _failure("UNPIVOT does not produce a dynamic column family") - pivot_columns, aggregates, dynamic, pivot_columns_valid = _pivot_components( - pivot=pivot, dialect=dialect - ) - if not dynamic: - return _failure("static pivot values must use ordinary exact column declarations") - if len(pivot_columns) != 1: - return _failure("dynamic column contracts currently require exactly one pivot column") - if not pivot_columns_valid: - return _failure("dynamic pivot columns must be direct column references") - source_node: object = pivot.get("this") - source_columns: tuple[_ColumnFact, ...] | None = _relation_schema( - node=source_node, - ctes=ctes, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - seen=frozenset(), - ) - if source_columns is None: - return _failure("dynamic pivot input does not have an authoritative, explicit schema") - input_relation: str | None = _external_relation_name( - node=source_node, - ctes=ctes, - seen=frozenset(), - ) - if input_relation is None: - return _failure("dynamic pivot input relation could not be resolved") - pivot_column: str = pivot_columns[0] - matched_aggregate_indices: set[int] = set() - family_proofs: list[DynamicColumnFamilyProof] = [] - for family in families: - if not family.pivot_column.casefold() == pivot_column.casefold(): - return _failure( - f"dynamic family '{family.name}' declares pivot_column '{family.pivot_column}' " - f"but the output pivot uses '{pivot_column}'" - ) - matches: list[tuple[int, str | None]] = [] - for index, aggregate in enumerate(aggregates): - aggregate_name, value_name, inferred_type = _aggregate_fact( - aggregate=aggregate, source_columns=source_columns - ) - if ( - aggregate_name.casefold() == family.aggregate.casefold() - and value_name is not None - and value_name.casefold() == family.value_column.casefold() - ): - matches.append((index, inferred_type)) - if len(matches) != 1: - return _failure( - f"dynamic family '{family.name}' must match exactly one " - f"{family.aggregate}({family.value_column}) pivot aggregate" - ) - aggregate_index, inferred_type = matches[0] - if aggregate_index in matched_aggregate_indices: - return _failure("one pivot aggregate cannot satisfy multiple dynamic families") - matched_aggregate_indices.add(aggregate_index) - family_proofs.append( - DynamicColumnFamilyProof(name=family.name, inferred_type=inferred_type) - ) - if len(matched_aggregate_indices) != len(aggregates): - return _failure("every dynamic pivot aggregate must have one declared column family") - fixed_names: tuple[str, ...] - if dialect in _SIMPLIFIED_PIVOT_DIALECTS and isinstance(pivot.get("group"), dict): - fixed_names = tuple( - name - for expression in _nested_list(node=pivot.get("group"), keys=("group", "expressions")) - if (name := _column_name(expression)) is not None - ) - else: - excluded: frozenset[str] = frozenset( - {pivot_column.casefold(), *(family.value_column.casefold() for family in families)} - ) - fixed_names = tuple( - column.name for column in source_columns if column.name.casefold() not in excluded - ) - source_by_name: dict[str, _ColumnFact] = { - column.name.casefold(): column for column in source_columns - } - if any(name.casefold() not in source_by_name for name in fixed_names): - return _failure("dynamic pivot grouping columns are not present in the pivot input") - return DynamicColumnContractProof( - output_proven=True, - fixed_columns=tuple( - InferredColumn( - name=name, - type=source_by_name[name.casefold()].type, - nullability=source_by_name[name.casefold()].nullability, - ) - for name in fixed_names - ), - families=tuple(family_proofs), - input_relations=(input_relation,), - bare_dynamic_pivot=bare_dynamic_pivot, - ) - - -def _pivot_components( - *, pivot: dict[str, object], dialect: str -) -> tuple[tuple[str, ...], tuple[dict[str, object], ...], bool, bool]: - if dialect in _SIMPLIFIED_PIVOT_DIALECTS and pivot.get("using"): - pivot_expressions: tuple[dict[str, object], ...] = _dict_list(pivot.get("expressions")) - pivot_columns: tuple[str, ...] = tuple( - name - for expression in pivot_expressions - if (name := _column_name(expression)) is not None - ) - return ( - pivot_columns, - _dict_list(pivot.get("using")), - not bool(pivot.get("fields")), - len(pivot_columns) == len(pivot_expressions), - ) - fields: tuple[dict[str, object], ...] = _dict_list(pivot.get("fields")) - pivot_columns = tuple( - name - for field in fields - if (name := _column_name(_nested(node=field, keys=("in", "this")))) is not None - ) - dynamic: bool = _has_dynamic_value_source(fields=fields) - return ( - pivot_columns, - _dict_list(pivot.get("expressions")), - dynamic, - len(pivot_columns) == len(fields), - ) - - -def _has_dynamic_value_source(*, fields: tuple[dict[str, object], ...]) -> bool: - for field in fields: - values: tuple[dict[str, object], ...] = _nested_list(node=field, keys=("in", "expressions")) - if any(_node_key(value) in _DYNAMIC_VALUE_SOURCE_KINDS for value in values): - return True - return False - - -def _aggregate_fact( - *, aggregate: dict[str, object], source_columns: tuple[_ColumnFact, ...] -) -> tuple[str, str | None, str | None]: - aggregate_name: str = _node_key(aggregate).upper() - payload: object = aggregate.get(_node_key(aggregate)) - if not isinstance(payload, dict): - return aggregate_name, None, None - payload_dict: dict[str, object] = cast(dict[str, object], payload) - authored_name: object = payload_dict.get("name") - if isinstance(authored_name, str) and authored_name: - aggregate_name = authored_name.upper() - value: object = payload_dict.get("this") - value_name: str | None = _column_name(value) - inferred_type: str | None = None - if _node_key(value) == _CAST_AST_KIND: - cast_payload: object = ( - cast(dict[str, object], value).get("cast") if isinstance(value, dict) else None - ) - if isinstance(cast_payload, dict): - cast_payload_dict: dict[str, object] = cast(dict[str, object], cast_payload) - value_name = _column_name(cast_payload_dict.get("this")) - if aggregate_name in _TYPE_PRESERVING_AGGREGATES: - inferred_type = _render_type(cast_payload_dict.get("to")) - if inferred_type is None and aggregate_name in _TYPE_PRESERVING_AGGREGATES: - source_by_name: dict[str, _ColumnFact] = { - column.name.casefold(): column for column in source_columns - } - source: _ColumnFact | None = ( - source_by_name.get(value_name.casefold()) if value_name is not None else None - ) - inferred_type = source.type if source is not None else None - return aggregate_name, value_name, inferred_type - - -def _resolve_boundary(*, node: object, ctes: _CteMap, seen: frozenset[str]) -> _Boundary | None: - key: str = _node_key(node) - payload: object = cast(dict[str, object], node).get(key) if isinstance(node, dict) else None - if key == _PIVOT_AST_KIND and isinstance(payload, dict): - return _Boundary(pivot=cast(dict[str, object], payload)) - if key in _QUERY_AST_KINDS and isinstance(payload, dict): - return _resolve_boundary( - node=cast(dict[str, object], payload).get("this"), ctes=ctes, seen=seen - ) - if key != _SELECT_AST_KIND or not isinstance(payload, dict): - return None - payload_dict: dict[str, object] = cast(dict[str, object], payload) - if payload_dict.get("joins") or not _is_single_wildcard(payload_dict.get("expressions")): - return None - relations: tuple[dict[str, object], ...] = _nested_dict_list( - node=payload_dict.get("from"), key="expressions" - ) - if len(relations) != 1: - return None - relation: dict[str, object] = relations[0] - if _node_key(relation) == _PIVOT_AST_KIND: - pivot_payload: object = relation.get("pivot") - return ( - _Boundary(pivot=cast(dict[str, object], pivot_payload)) - if isinstance(pivot_payload, dict) - else None - ) - relation_name: str | None = _relation_name(relation) - if relation_name is None: - return None - normalized_name: str = relation_name.casefold() - if normalized_name in ctes: - cte: dict[str, object] | None = ctes[normalized_name] - if cte is None: - return None - if relation_name.casefold() in seen: - return None - resolved: _Boundary | None = _resolve_boundary( - node=cte, - ctes=ctes, - seen=seen | {relation_name.casefold()}, - ) - if resolved is None: - return None - return resolved - return _Boundary(passthrough_name=relation_name) - - -def _relation_schema( - *, - node: object, - ctes: _CteMap, - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - seen: frozenset[str], -) -> tuple[_ColumnFact, ...] | None: - relation_name: str | None = _relation_name(node) - if relation_name is None: - return None - normalized_name: str = relation_name.casefold() - if normalized_name not in ctes: - return _external_schema( - name=relation_name, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - ) - cte: dict[str, object] | None = ctes[normalized_name] - if cte is None or normalized_name in seen: - return None - return _select_schema( - node=cte, - ctes=ctes, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - seen=seen | {normalized_name}, - ) - - -def _external_relation_name( - *, - node: object, - ctes: _CteMap, - seen: frozenset[str], -) -> str | None: - key: str = _node_key(node) - payload: object = cast(dict[str, object], node).get(key) if isinstance(node, dict) else None - if key in _QUERY_AST_KINDS and isinstance(payload, dict): - return _external_relation_name( - node=cast(dict[str, object], payload).get("this"), - ctes=ctes, - seen=seen, - ) - if key == _SELECT_AST_KIND and isinstance(payload, dict): - payload_dict: dict[str, object] = cast(dict[str, object], payload) - if payload_dict.get("joins"): - return None - relations: tuple[dict[str, object], ...] = _nested_dict_list( - node=payload_dict.get("from"), key="expressions" - ) - if len(relations) != 1: - return None - return _external_relation_name(node=relations[0], ctes=ctes, seen=seen) - relation_name: str | None = _relation_name(node) - if relation_name is None: - return None - normalized_name: str = relation_name.casefold() - if normalized_name not in ctes: - return relation_name - cte: dict[str, object] | None = ctes[normalized_name] - if cte is None or normalized_name in seen: - return None - return _external_relation_name( - node=cte, - ctes=ctes, - seen=seen | {normalized_name}, - ) - - -def _select_schema( - *, - node: object, - ctes: _CteMap, - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - seen: frozenset[str], -) -> tuple[_ColumnFact, ...] | None: - key: str = _node_key(node) - payload: object = cast(dict[str, object], node).get(key) if isinstance(node, dict) else None - if key in _QUERY_AST_KINDS and isinstance(payload, dict): - return _select_schema( - node=cast(dict[str, object], payload).get("this"), - ctes=ctes, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - seen=seen, - ) - if key != _SELECT_AST_KIND or not isinstance(payload, dict): - return None - payload_dict: dict[str, object] = cast(dict[str, object], payload) - if payload_dict.get("joins"): - return None - relations: tuple[dict[str, object], ...] = _nested_dict_list( - node=payload_dict.get("from"), key="expressions" - ) - if len(relations) != 1: - return None - source: tuple[_ColumnFact, ...] | None = _relation_schema( - node=relations[0], - ctes=ctes, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - seen=seen, - ) - if source is None: - return None - expressions: object = payload_dict.get("expressions") - if _is_single_wildcard(expressions): - return source - if not isinstance(expressions, list): - return None - source_by_name: dict[str, _ColumnFact] = {column.name.casefold(): column for column in source} - output: list[_ColumnFact] = [] - for raw_expression in expressions: - if not isinstance(raw_expression, dict): - return None - output_name, expression = _projection(cast(dict[str, object], raw_expression)) - source_name: str | None - expression_type: str | None = None - if _node_key(expression) == _CAST_AST_KIND: - cast_payload: object = expression.get("cast") - if not isinstance(cast_payload, dict): - return None - cast_payload_dict: dict[str, object] = cast(dict[str, object], cast_payload) - source_name = _column_name(cast_payload_dict.get("this")) - expression_type = _render_type(cast_payload_dict.get("to")) - else: - source_name = _column_name(expression) - if output_name is None or source_name is None: - return None - source_column: _ColumnFact | None = source_by_name.get(source_name.casefold()) - if source_column is None: - return None - if expression_type is None: - expression_type = source_column.type - output.append( - _ColumnFact( - name=output_name, - type=expression_type, - nullability=source_column.nullability, - ) - ) - return tuple(output) - - -def _external_schema( - *, - name: str, - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> tuple[_ColumnFact, ...] | None: - types: dict[str, str] | None = _casefold_lookup(mapping=column_types_by_table, key=name) - if types is None: - return None - nullability: dict[str, InferredNullability] = ( - _casefold_lookup(mapping=column_nullability_by_table, key=name) or {} - ) - nullability_by_name: dict[str, InferredNullability] = { - column_name.casefold(): value for column_name, value in nullability.items() - } - return tuple( - _ColumnFact( - name=column_name, - type=None if data_type == _UNKNOWN_DATA_TYPE else data_type, - nullability=nullability_by_name.get( - column_name.casefold(), InferredNullability.UNKNOWN - ), - ) - for column_name, data_type in types.items() - ) - - -def _collect_ctes(root: dict[str, object]) -> _CteMap: - key: str = _node_key(root) - payload: object = root.get(key) - if not isinstance(payload, dict): - return {} - payload_dict: dict[str, object] = cast(dict[str, object], payload) - with_payload: object = payload_dict.get("with") - if not isinstance(with_payload, dict): - return {} - with_payload_dict: dict[str, object] = cast(dict[str, object], with_payload) - ctes: _CteMap = {} - for cte in _dict_list(with_payload_dict.get("ctes")): - cte_payload: object = cte.get("cte") if _node_key(cte) == _CTE_AST_KIND else cte - if not isinstance(cte_payload, dict): - continue - cte_payload_dict: dict[str, object] = cast(dict[str, object], cte_payload) - name: str | None = _identifier_name(cte_payload_dict.get("alias")) - query: object = cte_payload_dict.get("this") - if name is None: - continue - if cte_payload_dict.get("columns"): - ctes[name.casefold()] = None - elif isinstance(query, dict): - ctes[name.casefold()] = cast(dict[str, object], query) - return ctes - - -def _projection(node: dict[str, object]) -> tuple[str | None, dict[str, object]]: - if _node_key(node) != _ALIAS_AST_KIND: - return _column_name(node), node - payload: object = node.get("alias") - if not isinstance(payload, dict): - return None, node - payload_dict: dict[str, object] = cast(dict[str, object], payload) - expression: object = payload_dict.get("this") - return ( - _identifier_name(payload_dict.get("alias")), - cast(dict[str, object], expression) if isinstance(expression, dict) else {}, - ) - - -def _relation_name(node: object) -> str | None: - key: str = _node_key(node) - payload: object = cast(dict[str, object], node).get(key) if isinstance(node, dict) else None - if not isinstance(payload, dict): - return None - payload_dict: dict[str, object] = cast(dict[str, object], payload) - if key == _TABLE_AST_KIND: - return _identifier_name(payload_dict.get("name")) - if key == _COLUMN_AST_KIND: - return _identifier_name(payload_dict.get("name")) - if ( - key == _FUNCTION_AST_KIND - and str(payload_dict.get("name", "")).casefold() in _DEPENDENCY_FUNCTION_NAMES - ): - arguments: tuple[dict[str, object], ...] = _dict_list(payload_dict.get("args")) - return _column_name(arguments[0]) if arguments else None - return None - - -def _column_name(node: object) -> str | None: - key: str = _node_key(node) - payload: object = cast(dict[str, object], node).get(key) if isinstance(node, dict) else None - if key == _COLUMN_AST_KIND and isinstance(payload, dict): - return _identifier_name(cast(dict[str, object], payload).get("name")) - return None - - -def _identifier_name(node: object) -> str | None: - if isinstance(node, str): - return node - if not isinstance(node, dict): - return None - value: object = cast(dict[str, object], node).get("name") - return value if isinstance(value, str) and value else None - - -def _render_type(node: object) -> str | None: - if not isinstance(node, dict): - return None - node_dict: dict[str, object] = cast(dict[str, object], node) - raw_type: object = node_dict.get("data_type") - if not isinstance(raw_type, str) or not raw_type: - return None - names: dict[str, str] = { - "big_int": "BIGINT", - "bool": "BOOLEAN", - "boolean": "BOOLEAN", - "decimal": "DECIMAL", - "double": "DOUBLE", - "float": "FLOAT", - "int": "INT", - "integer": "INT", - "text": "TEXT", - "timestamp": "TIMESTAMP", - "var_char": "VARCHAR", - "varchar": "VARCHAR", - } - rendered: str = names.get(raw_type.casefold(), raw_type.replace("_", " ").upper()) - precision: object = node_dict.get("precision") - scale: object = node_dict.get("scale") - length: object = node_dict.get("length") - if isinstance(length, int): - return f"{rendered}({length})" - if isinstance(precision, int): - return ( - f"{rendered}({precision}, {scale})" - if isinstance(scale, int) - else f"{rendered}({precision})" - ) - return rendered - - -def _families_equal( - *, - left: tuple[SchemaDynamicColumnFamily, ...], - right: tuple[SchemaDynamicColumnFamily, ...], -) -> bool: - def normalize(family: SchemaDynamicColumnFamily) -> tuple[str, str, str, str, str, str | None]: - return ( - family.name.casefold(), - family.pivot_column.casefold(), - family.value_column.casefold(), - family.aggregate.casefold(), - family.type.casefold(), - family.name_pattern, - ) - - return tuple(map(normalize, left)) == tuple(map(normalize, right)) - - -def _failure(reason: str) -> DynamicColumnContractProof: - return DynamicColumnContractProof(output_proven=False, failure_reason=reason) - - -def _node_key(node: object) -> str: - if not isinstance(node, dict) or len(node) != 1: - return "" - return str(next(iter(node))) - - -def _dict_list(value: object) -> tuple[dict[str, object], ...]: - if not isinstance(value, list): - return () - return tuple(cast(dict[str, object], item) for item in value if isinstance(item, dict)) - - -def _nested(*, node: object, keys: tuple[str, ...]) -> object: - current: object = node - for key in keys: - if not isinstance(current, dict): - return None - current = cast(dict[str, object], current).get(key) - return current - - -def _nested_list(*, node: object, keys: tuple[str, ...]) -> tuple[dict[str, object], ...]: - return _dict_list(_nested(node=node, keys=keys)) - - -def _nested_dict_list(*, node: object, key: str) -> tuple[dict[str, object], ...]: - return _dict_list(cast(dict[str, object], node).get(key) if isinstance(node, dict) else None) - - -def _is_single_wildcard(value: object) -> bool: - expressions: tuple[dict[str, object], ...] = _dict_list(value) - if len(expressions) != 1 or _node_key(expressions[0]) != _STAR_AST_KIND: - return False - payload: object = expressions[0].get("star") - if not isinstance(payload, dict): - return False - star: dict[str, object] = cast(dict[str, object], payload) - return not any(star.get(key) for key in ("except", "replace", "rename", "table")) - - -def _casefold_lookup[ValueT](*, mapping: dict[str, ValueT], key: str) -> ValueT | None: - requested: str = key.casefold() - for candidate, value in mapping.items(): - if candidate.casefold() == requested: - return value - return None diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/binding_waves.py b/src/sqlbuild/compiler/compile/_helpers/assembly/binding_waves.py deleted file mode 100644 index b084a8d591..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/binding_waves.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Analyze open model inputs after their producers have supplied closed shapes.""" - -from __future__ import annotations - -from collections.abc import Callable -from functools import partial - -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile.classes.binding_dataflow import BindingDataflow -from sqlbuild.compiler.compile.models import CompileModelInput, PolyglotAnalysisResult -from sqlbuild.compiler.compile.models import ( - ModelSqlAnalysis as _ModelSqlAnalysis, -) -from sqlbuild.compiler.compile.models import ( - ModelSqlAnalysisRequest as _ModelSqlAnalysisRequest, -) -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.references.types import SqlReferenceKind - -_DATAFLOW_WORKERS: int = 4 -_DATAFLOW_BATCH_MIN: int = 8 -_DATAFLOW_BATCH_LIMIT: int = 64 - - -def analyze_binding_waves( - *, - requests: tuple[_ModelSqlAnalysisRequest, ...], - names: tuple[str, ...], - cached: dict[str, PolyglotAnalysisResult], - previous_signatures: dict[str, str], - shapes: dict[str, dict[str, str]], - types: dict[str, dict[str, str]], - nullability: dict[str, dict[str, InferredNullability]], - profile: ExpressionInferenceProfile, - analyze: Callable[..., tuple[_ModelSqlAnalysis, ...]], - complete: Callable[..., tuple[_ModelSqlAnalysis, ...]], -) -> tuple[tuple[_ModelSqlAnalysis, ...], dict[str, PolyglotAnalysisResult]]: - """Analyze one topological level at a time, the reference for dataflow scheduling.""" - - return BindingDataflow( - requests=requests, - names=names, - cached=cached, - previous_signatures=previous_signatures, - shapes=shapes, - types=types, - nullability=nullability, - profile=profile, - analyze=analyze, - complete=complete, - ).analyze_waves() - - -def analyze_binding_dataflow( - *, - requests: tuple[_ModelSqlAnalysisRequest, ...], - names: tuple[str, ...], - cached: dict[str, PolyglotAnalysisResult], - previous_signatures: dict[str, str], - shapes: dict[str, dict[str, str]], - types: dict[str, dict[str, str]], - nullability: dict[str, dict[str, InferredNullability]], - profile: ExpressionInferenceProfile, - analyze: Callable[..., tuple[_ModelSqlAnalysis, ...]], - complete: Callable[..., tuple[_ModelSqlAnalysis, ...]], -) -> tuple[tuple[_ModelSqlAnalysis, ...], dict[str, PolyglotAnalysisResult]]: - """Analyze each model once its producers are final; replay waves after any error.""" - - create: Callable[[], BindingDataflow] = partial( - BindingDataflow, - requests=requests, - names=names, - cached=cached, - previous_signatures=previous_signatures, - shapes=shapes, - types=types, - nullability=nullability, - profile=profile, - analyze=analyze, - complete=complete, - ) - result: tuple[tuple[_ModelSqlAnalysis, ...], dict[str, PolyglotAnalysisResult]] | None = ( - create().analyze_dataflow( - workers=_DATAFLOW_WORKERS, - batch_min=_DATAFLOW_BATCH_MIN, - batch_limit=_DATAFLOW_BATCH_LIMIT, - ) - ) - return result if result is not None else create().analyze_waves() - - -def downstream_model_names( - *, model_inputs: tuple[CompileModelInput, ...], changed_names: set[str] -) -> set[str]: - downstream_by_name: dict[str, set[str]] = {} - for model_input in model_inputs: - for reference in model_input.references: - if reference.ref_kind == SqlReferenceKind.REF: - downstream_by_name.setdefault(reference.ref_name, set()).add( - model_input.model_file.file_path.stem - ) - downstream_names: set[str] = set() - pending: list[str] = list(changed_names) - while pending: - for downstream_name in downstream_by_name.get(pending.pop(), set()): - if downstream_name not in downstream_names and downstream_name not in changed_names: - downstream_names.add(downstream_name) - pending.append(downstream_name) - return downstream_names diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index f0e9290f73..33b2721369 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -2,41 +2,22 @@ from __future__ import annotations -import re from collections.abc import Sequence from dataclasses import dataclass, replace -from functools import partial -from graphlib import CycleError, TopologicalSorter from pathlib import Path from typing import Any, cast from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.contract.types import BuiltinAdapter +from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.compile._helpers.analysis.cache import ( build_analysis_cache_context, - build_compact_analysis_cache_plan, - compact_analysis_batch_response_writer, - model_analysis_cache_key, - model_analysis_output_signature, - read_compact_analysis_cache_candidate, - read_model_analyses, - record_analysis_cache_metrics, - write_model_analyses, ) from sqlbuild.compiler.compile._helpers.analysis.compact import ( - NativeCompactAnalysis, analyze_columns_and_lineage_with_polyglot, - analyze_queries_with_compact_polyglot_batch, get_complete_schema_binding_request, infer_columns_with_sql_analysis, ) -from sqlbuild.compiler.compile._helpers.analysis.dynamic_pivot import ( - analyze_dynamic_column_contract, -) -from sqlbuild.compiler.compile._helpers.analysis.pivot_requests import ( - model_dynamic_families, - model_pivot_sql, -) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import ( model_placeholders as _model_placeholders, ) @@ -48,28 +29,17 @@ get_authored_binding_location, query_line_offset, ) -from sqlbuild.compiler.compile._helpers.assembly.binding_waves import analyze_binding_dataflow -from sqlbuild.compiler.compile._helpers.assembly.binding_waves import ( - downstream_model_names as _downstream_model_names, -) from sqlbuild.compiler.compile._helpers.assembly.native_declarations import ( known_declared_types, known_function_names, ) from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( - binding_required_names, - binding_schema_for_model, build_column_nullability_by_table, build_complete_binding_schemas, - published_model_shape, - upstream_signatures, ) from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( build_declared_column_types as _build_column_types_by_table, ) -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( - referenced_model_names as _referenced_model_names, -) from sqlbuild.compiler.compile._helpers.assembly.targets import ( build_model_relation_target, build_seed_relation_target, @@ -86,8 +56,7 @@ from sqlbuild.compiler.compile._helpers.diagnostics.recovery import complete_semantic_diagnostics from sqlbuild.compiler.compile._helpers.diagnostics.scope import report_scope_index_errors from sqlbuild.compiler.compile._helpers.native_stages.assembly import ( - analyze_model_sql_by_engine, - dynamic_column_contract_by_engine, + analyze_model_sql, expression_source_shapes_by_engine, project_facts_by_engine, ) @@ -107,7 +76,6 @@ find_macro_call_names, ) from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data -from sqlbuild.compiler.compile._helpers.sharing.binding import shareable_binding_prekeys from sqlbuild.compiler.compile._helpers.sql_tests.helper_ctes import ( report_mocks_reading_referencing_helpers, ) @@ -122,11 +90,6 @@ ) from sqlbuild.compiler.compile.models import ( AnalysisCacheContext, - CompactAnalysisCacheCandidate, - CompactAnalysisCacheModel, - CompactAnalysisCachePlan, - CompactBatchExecutionOptions, - CompactBatchPreparation, CompileAuditInput, CompiledAudit, CompiledDirectLogicSqlTestPayload, @@ -161,12 +124,8 @@ from sqlbuild.compiler.compile.models import ( ModelSqlAnalysis as _ModelSqlAnalysis, ) -from sqlbuild.compiler.compile.models import ( - ModelSqlAnalysisRequest as _ModelSqlAnalysisRequest, -) from sqlbuild.compiler.compile.types import ( AttachedAuditTargetKind, - CompactBatchResponseCallback, CompiledResourceType, DiagnosticPhase, DiagnosticSeverity, @@ -187,14 +146,10 @@ BINDING_UNKNOWN_TABLE_INTERNAL_CODE, NATIVE_DIALECT_ALIASES, ) -from sqlbuild.compiler.sql_analysis.exceptions import SqlAnalysisBoundaryError from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog from sqlbuild.compiler.sql_analysis.main._identifier_case import ignores_quoted_case -from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations from sqlbuild.compiler.sql_analysis.models import ( SqlBindingDiagnostic, - SqlBindingResult, - SqlSchemaValidationRequest, ) from sqlbuild.spec.contracts.main.resolve_effective_adapter_name import ( resolve_effective_adapter_name, @@ -344,32 +299,30 @@ def assemble_compiled_project( record_compile_timing("model_analysis_ms"), record_compile_cpu_timing("model_analysis_cpu_ms"), ): - python_analysis: partial[dict[str, _ModelSqlAnalysis]] = partial( - _analyze_model_sql_in_parallel, - known_functions=known_function_names(inputs.sql_function_inputs), - known_types=known_declared_types( - functions=inputs.sql_function_inputs, column_types=column_types_by_table - ), - model_inputs=tuple( - model_input - for model_input in inputs.model_inputs - if model_input.sql_validation_enabled - and ( - analysis_model_names is None - or _model_name(model_input) in analysis_model_names - ) - ), - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=profile, - allow_compact_analysis=allow_compact_analysis, - rich_type_inference=rich_type_inference, - analysis_cache=analysis_cache, - complete_binding_schemas=complete_binding_schemas, - ) - model_sql_analysis_by_name, native_session = analyze_model_sql_by_engine( - python_analysis=python_analysis, - dynamic_families_by_table=dynamic_families_by_table, + model_sql_analysis_by_name, native_session = analyze_model_sql( + NativeModelAnalysisRequest( + known_functions=known_function_names(inputs.sql_function_inputs), + known_types=known_declared_types( + functions=inputs.sql_function_inputs, column_types=column_types_by_table + ), + model_inputs=tuple( + model_input + for model_input in inputs.model_inputs + if model_input.sql_validation_enabled + and ( + analysis_model_names is None + or _model_name(model_input) in analysis_model_names + ) + ), + column_nullability_by_table=column_nullability_by_table, + column_types_by_table=column_types_by_table, + inference_profile=profile, + allow_compact_analysis=allow_compact_analysis, + rich_type_inference=rich_type_inference, + analysis_cache=analysis_cache, + complete_binding_schemas=complete_binding_schemas, + dynamic_families_by_table=dynamic_families_by_table, + ) ) native: NativeProjectFacts | None = project_facts_by_engine( inputs=inputs, @@ -614,27 +567,12 @@ def _assemble_compiled_model( placeholders=placeholders, dialect=profile.sql_analysis_dialect, ) - dynamic_column_contract: DynamicColumnContractProof | None = dynamic_column_contract_by_engine( - sql_analysis=sql_analysis, - native_proof=native_model.dynamic_contract if native_model else None, - python_proof=partial( - analyze_dynamic_column_contract, - query_sql=model_pivot_sql(query_sql=analysis_query_sql, placeholders=placeholders), - dialect=profile.sql_analysis_dialect, - families=model_dynamic_families(model_input), - column_types_by_table=column_types_by_table or {}, - authoritative_column_types_by_table=( - dynamic_contract_analysis_inputs.authoritative_column_types_by_table - if dynamic_contract_analysis_inputs is not None - else {} - ), - column_nullability_by_table=column_nullability_by_table or {}, - dynamic_families_by_table=( - dynamic_contract_analysis_inputs.families_by_table - if dynamic_contract_analysis_inputs is not None - else {} - ), - ), + dynamic_column_contract: DynamicColumnContractProof | None = ( + sql_analysis.dynamic_column_contract + if sql_analysis is not None and sql_analysis.dynamic_column_contract is not None + else native_model.dynamic_contract + if native_model is not None + else None ) if dynamic_column_contract is not None and dynamic_column_contract.output_proven: inferred_columns = dynamic_column_contract.fixed_columns @@ -677,734 +615,6 @@ def _assemble_compiled_model( ) -def _analyze_model_sql_in_parallel( - *, - known_functions: tuple[str, ...], - known_types: tuple[str, ...], - model_inputs: tuple[CompileModelInput, ...], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - allow_compact_analysis: bool, - rich_type_inference: bool, - analysis_cache: AnalysisCacheContext | None, - complete_binding_schemas: dict[str, dict[str, str]], -) -> dict[str, _ModelSqlAnalysis]: - if not model_inputs: - return {} - analyzed_model_names: frozenset[str] = frozenset( - _model_name(model_input) for model_input in model_inputs - ) - requests: tuple[_ModelSqlAnalysisRequest, ...] = tuple( - _model_sql_analysis_request( - model_input=model_input, - analysis_cache=analysis_cache, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - complete_binding_schemas=complete_binding_schemas, - ) - for model_input in model_inputs - ) - request_cache_keys: tuple[str, ...] = tuple( - request.cache_key for request in requests if request.cache_key is not None - ) - dependency_ordered: bool = allow_compact_analysis and any( - request.binding_schema is not None and not all(request.binding_schema.values()) - for request in requests - ) - compact_batch_plan: CompactAnalysisCachePlan | None = None - if analysis_cache is not None: - compact_batch_plan = build_compact_analysis_cache_plan( - context=analysis_cache, - models=tuple( - CompactAnalysisCacheModel( - name=_model_name(request.model_input), - cache_key=request.cache_key, - upstream_names=_referenced_model_names( - model_input=request.model_input, - available_names=analyzed_model_names, - ), - ) - for request in requests - ), - min_model_count=_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT, - ) - compact_cached_analyses: dict[str, PolyglotAnalysisResult] = {} - compact_batch_hit_count: int = 0 - compact_candidate: CompactAnalysisCacheCandidate | None = ( - read_compact_analysis_cache_candidate( - plan=compact_batch_plan, - expected_count=len(requests), - min_model_count=_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT, - ) - if compact_batch_plan is not None and not dependency_ordered - else None - ) - if compact_candidate is not None: - completed_analyses: dict[str, PolyglotAnalysisResult] = {} - if analysis_cache is not None: - completed_analyses, _, _ = read_model_analyses( - context=analysis_cache, - cache_keys=request_cache_keys, - model_names=tuple(_model_name(request.model_input) for request in requests), - upstream_model_names_by_key={ - request.cache_key: _referenced_model_names( - model_input=request.model_input, - available_names=analyzed_model_names, - ) - for request in requests - if request.cache_key is not None - }, - ) - if all(key in completed_analyses for key in request_cache_keys): - record_analysis_cache_metrics( - batch_hits=len(requests), entry_hits=0, misses=0, bypasses=0 - ) - return { - _model_name(request.model_input): _ModelSqlAnalysis( - polyglot_analysis=completed_analyses[cache_key], - placeholders=request.placeholders, - ) - for request, cache_key in zip(requests, request_cache_keys, strict=True) - } - try: - compact_analyses: tuple[_ModelSqlAnalysis, ...] = _analyze_model_sql_requests( - requests=requests, - cached_analyses=completed_analyses, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - rich_type_inference=rich_type_inference, - cached_compact_batch=( - compact_candidate.preparation, - compact_candidate.response, - ), - ) - except SqlAnalysisBoundaryError: - pass - else: - compact_cached_analyses = { - request_cache_keys[index]: compact_analyses[index].polyglot_analysis - for index in compact_candidate.matching_indexes - } - compact_batch_hit_count = len(compact_candidate.matching_indexes) - if compact_batch_hit_count == len(requests): - record_analysis_cache_metrics( - batch_hits=compact_batch_hit_count, - entry_hits=0, - misses=0, - bypasses=0, - ) - compact_analyses = _complete_inferred_bindings( - known_functions=known_functions, - known_types=known_types, - requests=requests, - analyses=compact_analyses, - complete_binding_schemas=complete_binding_schemas, - inference_profile=inference_profile, - ) - return { - _model_name(model_input): analysis - for model_input, analysis in zip(model_inputs, compact_analyses, strict=True) - } - entry_cache_keys: tuple[str, ...] = tuple( - cache_key for cache_key in request_cache_keys if cache_key not in compact_cached_analyses - ) - cached_analyses: dict[str, PolyglotAnalysisResult] - previous_signatures: dict[str, str] - cached_output_signatures_by_key: dict[str, str] - cached_analyses, previous_signatures, cached_output_signatures_by_key = ( - read_model_analyses( - context=analysis_cache, - cache_keys=entry_cache_keys, - model_names=tuple(_model_name(request.model_input) for request in requests), - upstream_model_names_by_key={ - request.cache_key: _referenced_model_names( - model_input=request.model_input, - available_names=analyzed_model_names, - ) - for request in requests - if request.cache_key is not None - and request.cache_key not in compact_cached_analyses - }, - ) - if analysis_cache is not None - else ({}, {}, {}) - ) - entry_cache_hit_count: int = sum( - request.cache_key is not None and request.cache_key in cached_analyses - for request in requests - ) - if ( - compact_batch_plan is not None - and compact_batch_hit_count == 0 - and entry_cache_hit_count > 0 - and entry_cache_hit_count * 100 < len(requests) * _COMPACT_BATCH_ENTRY_REUSE_MIN_PERCENT - ): - cached_analyses.clear() - cached_output_signatures_by_key.clear() - entry_cache_hit_count = 0 - cached_analyses.update(compact_cached_analyses) - cached_output_signatures_by_key.update( - { - cache_key: model_analysis_output_signature(analysis) - for cache_key, analysis in compact_cached_analyses.items() - } - ) - analyses: tuple[_ModelSqlAnalysis, ...] - if dependency_ordered: - analyses, cached_analyses = analyze_binding_dataflow( - requests=requests, - names=tuple(_model_name(request.model_input) for request in requests), - cached=cached_analyses, - previous_signatures=previous_signatures, - shapes=complete_binding_schemas, - types=column_types_by_table, - nullability=column_nullability_by_table, - profile=inference_profile, - analyze=partial( - _analyze_model_sql_requests, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - rich_type_inference=rich_type_inference, - shareable_prekeys=shareable_binding_prekeys(requests=requests), - ), - complete=partial( - _complete_inferred_bindings, - known_functions=known_functions, - known_types=known_types, - inference_profile=inference_profile, - ), - ) - if compact_batch_plan is not None: - compact_batch_hit_count = sum(analysis.cached for analysis in analyses) - entry_cache_hit_count -= compact_batch_hit_count - else: - analyses = _analyze_model_sql_requests( - requests=requests, - cached_analyses=cached_analyses, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - rich_type_inference=rich_type_inference, - on_compact_response=( - compact_analysis_batch_response_writer( - plan=compact_batch_plan, - count=len(requests), - ) - if compact_batch_plan is not None and not cached_analyses - else None - ), - shareable_prekeys=shareable_binding_prekeys(requests=requests), - ) - analyses = _complete_inferred_bindings( - known_functions=known_functions, - known_types=known_types, - requests=requests, - analyses=analyses, - complete_binding_schemas=complete_binding_schemas, - inference_profile=inference_profile, - ) - record_analysis_cache_metrics( - batch_hits=compact_batch_hit_count, - entry_hits=entry_cache_hit_count, - misses=( - len(requests) - compact_batch_hit_count - entry_cache_hit_count - if analysis_cache is not None - else 0 - ), - bypasses=(len(requests) if analysis_cache is None else 0), - ) - if analysis_cache is None: - return { - _model_name(model_input): analysis - for model_input, analysis in zip(model_inputs, analyses, strict=True) - } - current_analyses_by_name: dict[str, PolyglotAnalysisResult] = { - _model_name(request.model_input): analysis.polyglot_analysis - for request, analysis in zip(requests, analyses, strict=True) - } - current_signatures_by_name: dict[str, str] = { - _model_name(request.model_input): ( - cached_output_signatures_by_key[request.cache_key] - if request.cache_key is not None and request.cache_key in cached_analyses - else model_analysis_output_signature(analysis.polyglot_analysis) - ) - for request, analysis in zip(requests, analyses, strict=True) - } - analyses_to_record_by_name: dict[str, PolyglotAnalysisResult] = { - _model_name(request.model_input): analysis.polyglot_analysis - for request, analysis in zip(requests, analyses, strict=True) - if request.cache_key is None or request.cache_key not in cached_analyses - } - changed_signature_names: set[str] = { - _model_name(request.model_input) - for request, analysis in zip(requests, analyses, strict=True) - if ( - previous_signatures.get(_model_name(request.model_input)) - != current_signatures_by_name[_model_name(request.model_input)] - or ( - not analysis.polyglot_analysis.analysis_succeeded - and request.cache_key not in cached_analyses - ) - ) - } - analyses_to_record_by_name.update( - {model_name: current_analyses_by_name[model_name] for model_name in changed_signature_names} - ) - invalidated_names: set[str] = _downstream_model_names( - model_inputs=model_inputs, - changed_names=changed_signature_names, - ) - if invalidated_names and not dependency_ordered: - invalidated_requests: tuple[_ModelSqlAnalysisRequest, ...] = tuple( - request - for request in requests - if ( - _model_name(request.model_input) in invalidated_names - and request.cache_key is not None - and request.cache_key in cached_analyses - ) - ) - invalidated_analyses_by_name: dict[str, _ModelSqlAnalysis] = { - _model_name(request.model_input): analysis - for request, analysis in zip( - invalidated_requests, - _analyze_model_sql_requests( - requests=invalidated_requests, - cached_analyses={}, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - rich_type_inference=rich_type_inference, - ), - strict=True, - ) - } - analyses = tuple( - invalidated_analyses_by_name.get(_model_name(request.model_input), analysis) - for request, analysis in zip(requests, analyses, strict=True) - ) - analyses = _complete_inferred_bindings( - known_functions=known_functions, - known_types=known_types, - requests=requests, - analyses=analyses, - complete_binding_schemas=complete_binding_schemas, - inference_profile=inference_profile, - ) - invalidated_analyses_by_name = { - _model_name(request.model_input): analysis - for request, analysis in zip(requests, analyses, strict=True) - if _model_name(request.model_input) in invalidated_names - } - analyses_to_record_by_name.update( - { - _model_name(request.model_input): analysis.polyglot_analysis - for request, analysis in zip(requests, analyses, strict=True) - if _model_name(request.model_input) in invalidated_names - } - ) - current_analyses_by_name = { - _model_name(request.model_input): analysis.polyglot_analysis - for request, analysis in zip(requests, analyses, strict=True) - } - current_signatures_by_name.update( - { - model_name: model_analysis_output_signature(invalidated_analysis.polyglot_analysis) - for model_name, invalidated_analysis in invalidated_analyses_by_name.items() - } - ) - with record_compile_timing("cache_publication_ms"): - write_model_analyses( - context=analysis_cache, - analyses_by_key={ - request.cache_key: analysis.polyglot_analysis - for request, analysis in zip(requests, analyses, strict=True) - if request.cache_key is not None - and ( - request.cache_key not in cached_analyses - or _model_name(request.model_input) in invalidated_names - ) - }, - latest_analyses_by_model=analyses_to_record_by_name, - dependency_signatures_by_key={ - request.cache_key: upstream_signatures( - model_input=request.model_input, - signatures=current_signatures_by_name, - available_names=analyzed_model_names, - ) - for request in requests - if request.cache_key is not None - and ( - request.cache_key not in cached_analyses - or _model_name(request.model_input) in invalidated_names - ) - }, - ) - return { - _model_name(model_input): analysis - for model_input, analysis in zip(model_inputs, analyses, strict=True) - } - - -def _analyze_model_sql_requests( - *, - requests: tuple[_ModelSqlAnalysisRequest, ...], - cached_analyses: dict[str, PolyglotAnalysisResult], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - allow_compact_analysis: bool, - rich_type_inference: bool, - cached_compact_batch: tuple[CompactBatchPreparation, object] | None = None, - on_compact_response: CompactBatchResponseCallback | None = None, - shareable_prekeys: frozenset[str] | None = None, -) -> tuple[_ModelSqlAnalysis, ...]: - if allow_compact_analysis: - uncached: tuple[tuple[int, _ModelSqlAnalysisRequest], ...] = tuple( - (index, request) - for index, request in enumerate(requests) - if request.cache_key is None or request.cache_key not in cached_analyses - ) - prepared_by_index: dict[int, NativeCompactAnalysis] = {} - diagnostics_by_index: dict[int, tuple[SqlBindingDiagnostic, ...]] = {} - if uncached: - batch_requests: tuple[tuple[int, _ModelSqlAnalysisRequest], ...] = ( - tuple(enumerate(requests)) if cached_compact_batch is not None else uncached - ) - prepared: tuple[NativeCompactAnalysis, ...] = ( - analyze_queries_with_compact_polyglot_batch( - query_sqls=tuple(request.query_sql for _, request in batch_requests), - references=tuple( - request.model_input.references for _, request in batch_requests - ), - placeholders=tuple(request.placeholders for _, request in batch_requests), - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - recover_cte_facts=tuple( - _should_recover_cte_facts(request.model_input) - for _, request in batch_requests - ), - rich_type_inference=rich_type_inference, - cached_batch=cached_compact_batch, - execution=CompactBatchExecutionOptions( - on_response=( - on_compact_response if len(uncached) == len(requests) else None - ), - binding_schemas=tuple( - request.binding_schema for _, request in batch_requests - ), - shareable_prekeys=shareable_prekeys, - ), - ) - ) - prepared_by_index = { - index: value for (index, _), value in zip(batch_requests, prepared, strict=True) - } - validation_indices: tuple[int, ...] = tuple( - index - for index, request in uncached - if request.binding_schema is not None - and prepared_by_index[index].binding_diagnostics is None - ) - with record_compile_timing("binding_validation_ms"): - validation_results: tuple[SqlBindingResult, ...] = get_schema_validations( - requests=tuple( - SqlSchemaValidationRequest( - sql=prepared_by_index[index].cleaned_sql, - dialect=inference_profile.sql_analysis_dialect, - schema=requests[index].binding_schema or {}, - known_functions=inference_profile.semantic_known_functions, - known_types=inference_profile.semantic_known_types, - quoted_identifiers_ignore_case=inference_profile.quoted_identifiers_ignore_case, - catalog=inference_profile.binding_catalog, - ) - for index in validation_indices - ) - ) - diagnostics_by_index = { - index: result.diagnostics - for index, result in zip(validation_indices, validation_results, strict=True) - } - return tuple( - _analyze_model_sql( - request=request, - cached_analysis=( - cached_analyses.get(request.cache_key) - if request.cache_key is not None - else None - ), - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=True, - precomputed=( - replace( - prepared_by_index[index], - binding_diagnostics=diagnostics_by_index.get( - index, prepared_by_index[index].binding_diagnostics - ), - ) - if index in prepared_by_index - else None - ), - ) - for index, request in enumerate(requests) - ) - - def analyze(request: _ModelSqlAnalysisRequest) -> _ModelSqlAnalysis: - return _analyze_model_sql( - request=request, - cached_analysis=( - cached_analyses.get(request.cache_key) if request.cache_key is not None else None - ), - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - ) - - return tuple(analyze(request) for request in requests) - - -def _complete_inferred_bindings( - *, - known_functions: tuple[str, ...], - known_types: tuple[str, ...], - requests: tuple[_ModelSqlAnalysisRequest, ...], - analyses: tuple[_ModelSqlAnalysis, ...], - complete_binding_schemas: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - supplied_relations: frozenset[str] = frozenset(), -) -> tuple[_ModelSqlAnalysis, ...]: - """Bind each scope independently, preserving open inputs as empty tables.""" - - complete_schemas: dict[str, dict[str, str]] = dict(complete_binding_schemas) - results: list[_ModelSqlAnalysis] = list(analyses) - indexes: dict[str, int] = { - _model_name(request.model_input): index for index, request in enumerate(requests) - } - available_names: frozenset[str] = frozenset(indexes) - graph: dict[str, tuple[str, ...]] = { - name: _referenced_model_names( - model_input=requests[index].model_input, available_names=available_names - ) - for name, index in indexes.items() - } - try: - ordered_names: tuple[str, ...] = tuple(TopologicalSorter(graph).static_order()) - except CycleError: - return analyses - for name in ordered_names: - index: int = indexes[name] - request: _ModelSqlAnalysisRequest = requests[index] - required_names: frozenset[str] | None = binding_required_names(request.model_input) - if required_names is None: - continue - analysis: PolyglotAnalysisResult = results[index].polyglot_analysis - inputs_known: bool = bool(required_names) and required_names <= complete_schemas.keys() - star_pending: bool = analysis.has_star and not analysis.star_resolved and inputs_known - stars_expanded_natively: bool = required_names <= supplied_relations - if star_pending and analysis.columns and stars_expanded_natively: - analysis = replace(analysis, star_resolved=True) - results[index] = replace(results[index], polyglot_analysis=analysis) - star_pending = False - if ( - not results[index].cached - and required_names - and (any(column.type is None for column in analysis.columns or ()) or star_pending) - and not re.search(r"\b(?:UNION|INTERSECT|EXCEPT)\b", request.query_sql, re.IGNORECASE) - ): - input_schemas: dict[str, dict[str, str]] = { - table: complete_schemas[table] - for table in required_names - if table in complete_schemas - } - nullability: dict[str, dict[str, InferredNullability]] = { - table: dict.fromkeys(columns, InferredNullability.UNKNOWN) - for table, columns in input_schemas.items() - } - enriched: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=request.query_sql, - references=request.model_input.references, - placeholders=request.placeholders, - column_types_by_table=input_schemas, - column_nullability_by_table=nullability, - inference_profile=inference_profile, - allow_compact_analysis=True, - recover_cte_facts=_should_recover_cte_facts(request.model_input), - ) - recovered_types: dict[str, str | None] = { - column.name: column.type - for column in (*(enriched.columns or ()), *(analysis.columns or ())) - if column.type is not None - } - star_expanded: bool = star_pending and enriched.analysis_succeeded - if (not analysis.columns or star_expanded) and enriched.analysis_succeeded: - analysis = replace( - enriched, - binding_diagnostics=analysis.binding_diagnostics, - binding_validated=analysis.binding_validated, - ) - analysis = replace( - analysis, - columns=tuple( - replace(column, type=column.type or recovered_types.get(column.name)) - for column in analysis.columns or () - ), - star_resolved=analysis.star_resolved or star_expanded, - ) - results[index] = replace(results[index], polyglot_analysis=analysis) - star_known: bool = not analysis.has_star or (analysis.star_resolved and inputs_known) - if analysis.columns and star_known and name not in complete_schemas: - complete_schemas.setdefault( - name, - published_model_shape( - sql=results[index].cleaned_sql or request.query_sql, - profile=inference_profile, - columns={column.name: column.type or "UNKNOWN" for column in analysis.columns}, - inputs={table: complete_schemas.get(table, {}) for table in required_names}, - config_values=request.model_input.config.values, - ), - ) - deferred_validation_indices: list[int] = [] - deferred_validation_requests: list[SqlSchemaValidationRequest] = [] - for index, request in enumerate(requests): - if results[index].cached: - continue - required_names = binding_required_names(request.model_input) - if required_names is None: - continue - if results[index].fused_binding_validated and ( - results[index].validated_schema or request.binding_schema - ) == {name: complete_schemas.get(name, {}) for name in required_names}: - continue - deferred_validation_indices.append(index) - deferred_validation_requests.append( - replace( - get_complete_schema_binding_request( - query_sql=request.query_sql, - cleaned_sql=results[index].cleaned_sql, - known_functions=known_functions, - known_types=known_types, - placeholders=request.placeholders, - dialect=inference_profile.sql_analysis_dialect, - binding_schema={ - name: complete_schemas.get(name, {}) for name in required_names - }, - ), - quoted_identifiers_ignore_case=inference_profile.quoted_identifiers_ignore_case, - catalog=inference_profile.binding_catalog, - ) - ) - with record_compile_timing("binding_validation_ms"): - validation_results: tuple[SqlBindingResult, ...] = get_schema_validations( - requests=tuple(deferred_validation_requests) - ) - for index, validation_result in zip( - deferred_validation_indices, validation_results, strict=True - ): - current: _ModelSqlAnalysis = results[index] - results[index] = replace( - current, - polyglot_analysis=replace( - current.polyglot_analysis, - binding_diagnostics=validation_result.diagnostics, - binding_validated=True, - ), - ) - return tuple(results) - - -def _analyze_model_sql( - *, - request: _ModelSqlAnalysisRequest, - cached_analysis: PolyglotAnalysisResult | None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - allow_compact_analysis: bool, - precomputed: NativeCompactAnalysis | None = None, -) -> _ModelSqlAnalysis: - if cached_analysis is not None: - return _ModelSqlAnalysis( - polyglot_analysis=cached_analysis, - placeholders=request.placeholders, - cached=True, - ) - polyglot_analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=request.query_sql, - references=request.model_input.references, - placeholders=request.placeholders, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - allow_compact_analysis=allow_compact_analysis, - binding_schema=request.binding_schema, - recover_cte_facts=_should_recover_cte_facts(request.model_input), - precomputed=precomputed, - ) - return _ModelSqlAnalysis( - polyglot_analysis=polyglot_analysis, - cleaned_sql=precomputed.cleaned_sql if precomputed is not None else None, - placeholders=request.placeholders, - validated_schema=request.binding_schema, - fused_binding_validated=precomputed is not None - and precomputed.binding_diagnostics is not None, - ) - - -def _model_sql_analysis_request( - *, - model_input: CompileModelInput, - analysis_cache: AnalysisCacheContext | None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - complete_binding_schemas: dict[str, dict[str, str]], -) -> _ModelSqlAnalysisRequest: - placeholders: dict[str, str] | None = _model_placeholders(model_input) - query_sql: str = cursor_intrinsics_analysis_sql( - sql=model_input.query_sql, - cursor_type=model_input.config.values.get("cursor_type"), - ) - binding_schema: dict[str, dict[str, str]] | None = binding_schema_for_model( - model_input=model_input, - complete_binding_schemas=complete_binding_schemas, - ) - cache_key: str | None = ( - model_analysis_cache_key( - context=analysis_cache, - query_sql=query_sql, - references=model_input.references, - placeholders=placeholders, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - binding_schema=binding_schema, - recover_cte_facts=_should_recover_cte_facts(model_input), - ) - if analysis_cache is not None - else None - ) - return _ModelSqlAnalysisRequest( - model_input=model_input, - query_sql=query_sql, - placeholders=placeholders, - cache_key=cache_key, - binding_schema=binding_schema, - ) - - -def _should_recover_cte_facts(model_input: CompileModelInput) -> bool: - return model_input.config.values.get("contract") == ContractPolicy.ENFORCED or ( - model_input.schema_entry is not None and bool(model_input.schema_entry.type_enforcement) - ) - - def _model_name(model_input: CompileModelInput) -> str: return model_input.model_file.file_path.stem diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 60a89be3cb..76e91b709a 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -2,7 +2,6 @@ from __future__ import annotations -from functools import partial from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile @@ -119,33 +118,10 @@ def expression_source_shapes_by_engine( return get_expression_source_shapes(expressions=expressions, profile=profile) -def analyze_model_sql_by_engine( - *, - python_analysis: partial[dict[str, ModelSqlAnalysis]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], +def analyze_model_sql( + request: NativeModelAnalysisRequest, ) -> tuple[dict[str, ModelSqlAnalysis], Any | None]: - """Analyze models natively, or with Python; also return the finished native session.""" - - native: NativeModelAnalyses | None = analyze_native_model_sql( - request=NativeModelAnalysisRequest( - **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table - ) - ) - if native is not None: - return native.analyses, native.session - return python_analysis(), None - - -def dynamic_column_contract_by_engine( - *, - sql_analysis: ModelSqlAnalysis | None, - native_proof: DynamicColumnContractProof | None, - python_proof: partial[DynamicColumnContractProof | None], -) -> DynamicColumnContractProof | None: - """Return the proof native analysis carries or proved alone, or prove it in Python.""" + """Analyze models natively; also return the finished session.""" - if sql_analysis is not None and sql_analysis.dynamic_column_contract is not None: - return sql_analysis.dynamic_column_contract - if native_proof is not None: - return native_proof - return python_proof() + native: NativeModelAnalyses = analyze_native_model_sql(request=request) + return native.analyses, native.session diff --git a/src/sqlbuild/compiler/compile/classes/binding_dataflow.py b/src/sqlbuild/compiler/compile/classes/binding_dataflow.py deleted file mode 100644 index 0909ba58c7..0000000000 --- a/src/sqlbuild/compiler/compile/classes/binding_dataflow.py +++ /dev/null @@ -1,314 +0,0 @@ -"""Model analysis state that publishes producer shapes as each analysis completes.""" - -from __future__ import annotations - -import contextvars -import heapq -import sys -import threading -import time -from collections.abc import Callable -from dataclasses import replace -from graphlib import CycleError, TopologicalSorter - -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.cache import model_analysis_output_signature -from sqlbuild.compiler.compile._helpers.analysis.compact import serialized_analysis -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( - binding_relation_names, - published_model_shape, - referenced_model_names, -) -from sqlbuild.compiler.compile.models import ( - ModelSqlAnalysis, - ModelSqlAnalysisRequest, - PolyglotAnalysisResult, -) -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.presentation.main.transient_line_coordinator import ( - shared_transient_line_coordinator, -) - -_WORKER_POLL_SECONDS: float = 0.05 -_WORKER_START_GRACE_SECONDS: float = 1.0 -_INTERRUPT_NOTICE: str = "Interrupted; finishing in-flight model analysis...\n" - - -class BindingDataflow: - """Analyze each model with exactly the producer shapes and cache state it depends on.""" - - def __init__( - self, - *, - requests: tuple[ModelSqlAnalysisRequest, ...], - names: tuple[str, ...], - cached: dict[str, PolyglotAnalysisResult], - previous_signatures: dict[str, str], - shapes: dict[str, dict[str, str]], - types: dict[str, dict[str, str]], - nullability: dict[str, dict[str, InferredNullability]], - profile: ExpressionInferenceProfile, - analyze: Callable[..., tuple[ModelSqlAnalysis, ...]], - complete: Callable[..., tuple[ModelSqlAnalysis, ...]], - ) -> None: - self._requests: tuple[ModelSqlAnalysisRequest, ...] = requests - self._names: tuple[str, ...] = names - self._cached: dict[str, PolyglotAnalysisResult] = cached - self._by_name: dict[str, ModelSqlAnalysisRequest] = dict(zip(names, requests, strict=True)) - available_names: frozenset[str] = frozenset(names) - self._dependencies: dict[str, tuple[str, ...]] = { - name: referenced_model_names( - model_input=request.model_input, available_names=available_names - ) - for name, request in self._by_name.items() - } - self._previous_signatures: dict[str, str] = previous_signatures - self._complete_shapes: dict[str, dict[str, str]] = dict(shapes) - self._available_types: dict[str, dict[str, str]] = dict(types) - self._available_nullability: dict[str, dict[str, InferredNullability]] = dict(nullability) - self._reusable: dict[str, PolyglotAnalysisResult] = dict(cached) - self._profile: ExpressionInferenceProfile = profile - self._analyze: Callable[..., tuple[ModelSqlAnalysis, ...]] = analyze - self._complete: Callable[..., tuple[ModelSqlAnalysis, ...]] = complete - self._changed: set[str] = set() - self._results: dict[str, ModelSqlAnalysis] = {} - self._ready: list[tuple[int, int, str]] = [] - self._pending: dict[str, int] = {} - self._dependents: dict[str, list[str]] = {} - self._failure: BaseException | None = None - self._cancelled: bool = False - self._condition: threading.Condition = threading.Condition(threading.Lock()) - - def analyze_waves( - self, - ) -> tuple[tuple[ModelSqlAnalysis, ...], dict[str, PolyglotAnalysisResult]]: - """Analyze one topological level at a time, the reference schedule.""" - - sorter: TopologicalSorter[str] = TopologicalSorter(self._dependencies) - try: - sorter.prepare() - except CycleError: - return self._analyze_cycle(), self._cached - while sorter.is_active(): - ready: tuple[str, ...] = tuple(sorter.get_ready()) - self._analyze_ready(ready) - sorter.done(*ready) - return tuple(self._results[name] for name in self._names), self._reusable - - def analyze_dataflow( - self, *, workers: int, batch_min: int, batch_limit: int - ) -> tuple[tuple[ModelSqlAnalysis, ...], dict[str, PolyglotAnalysisResult]] | None: - """Analyze models as their producers finish; return None after an analysis error.""" - - try: - order: tuple[str, ...] = tuple(TopologicalSorter(self._dependencies).static_order()) - except CycleError: - return self._analyze_cycle(), self._cached - heights: dict[str, int] = {} - self._dependents = {name: [] for name in self._by_name} - for name in self._by_name: - for parent in self._dependencies[name]: - self._dependents[parent].append(name) - for name in reversed(order): - heights[name] = 1 + max((heights[child] for child in self._dependents[name]), default=0) - priorities: dict[str, tuple[int, int]] = { - name: (-heights[name], position) for position, name in enumerate(self._by_name) - } - self._pending = {name: len(self._dependencies[name]) for name in self._by_name} - self._ready = [ - (*priorities[name], name) for name in self._by_name if not self._pending[name] - ] - heapq.heapify(self._ready) - share: tuple[int, int, int] = (workers, batch_min, batch_limit) - with serialized_analysis(self._condition): - context: contextvars.Context = contextvars.copy_context() - threads: list[threading.Thread] = [ - threading.Thread( - target=context.copy().run, - args=(self._run_worker,), - kwargs={"priorities": priorities, "share": share}, - name=f"sqlbuild-model-analysis-{index}", - daemon=True, - ) - for index in range(max(1, workers)) - ] - self._wait_for_workers(threads) - if self._failure is not None and not isinstance(self._failure, Exception): - raise self._failure - if self._failure is not None: - return None - return tuple(self._results[name] for name in self._names), self._reusable - - def _wait_for_workers(self, threads: list[threading.Thread]) -> None: - """Join workers lock-free; a first interrupt cancels them, repeats wait for their exit.""" - - attempted: list[threading.Thread] = [] - interrupt: BaseException | None = None - try: - for thread in threads: - attempted.append(thread) - thread.start() - except BaseException as error: # noqa: BLE001 - re-raised after workers stop - interrupt = self._cancel(interrupt=None, error=error) - pending: list[threading.Thread] = list(attempted) - unstarted_deadline: float = time.monotonic() + _WORKER_START_GRACE_SECONDS - while pending: - try: - pending = [ - thread - for thread in pending - if self._worker_running(thread=thread, unstarted_deadline=unstarted_deadline) - ] - except BaseException as error: # noqa: BLE001 - re-raised after workers stop - interrupt = self._cancel(interrupt=interrupt, error=error) - if interrupt is not None: - raise interrupt - - def _cancel(self, *, interrupt: BaseException | None, error: BaseException) -> BaseException: - if interrupt is not None: - return interrupt - self._cancelled = True - if not isinstance(error, Exception): - shared_transient_line_coordinator().write_persistent( - stream=sys.stderr, text=_INTERRUPT_NOTICE - ) - return error - - @staticmethod - def _worker_running(*, thread: threading.Thread, unstarted_deadline: float) -> bool: - """Join briefly; a thread whose OS thread never started is dropped after a grace period.""" - - if thread.ident is None: - time.sleep(_WORKER_POLL_SECONDS / 10) - return time.monotonic() < unstarted_deadline - try: - thread.join(_WORKER_POLL_SECONDS) - except RuntimeError: - return True - return thread.is_alive() - - def _run_worker( - self, *, priorities: dict[str, tuple[int, int]], share: tuple[int, int, int] - ) -> None: - with self._condition: - try: - self._drain_ready(priorities=priorities, share=share) - except BaseException as error: # noqa: BLE001 - re-raised or replayed in waves - self._record_failure(error) - self._condition.notify_all() - - def _record_failure(self, error: BaseException) -> None: - """Keep the first failure, except that a non-Exception interrupt always wins.""" - - if self._failure is None or ( - isinstance(self._failure, Exception) and not isinstance(error, Exception) - ): - self._failure = error - - def _drain_ready( - self, *, priorities: dict[str, tuple[int, int]], share: tuple[int, int, int] - ) -> None: - workers, batch_min, batch_limit = share - while ( - not self._cancelled - and self._failure is None - and len(self._results) < len(self._by_name) - ): - if not self._ready: - _ = self._condition.wait(_WORKER_POLL_SECONDS) - continue - count: int = min( - len(self._ready), - max(batch_min, -(-len(self._ready) // workers)), - batch_limit, - ) - batch: tuple[str, ...] = tuple(heapq.heappop(self._ready)[2] for _ in range(count)) - self._analyze_ready(batch) - for name in batch: - for child in self._dependents[name]: - self._pending[child] -= 1 - if not self._pending[child]: - heapq.heappush(self._ready, (*priorities[child], child)) - self._condition.notify_all() - - def _analyze_cycle(self) -> tuple[ModelSqlAnalysis, ...]: - return self._complete( - requests=self._requests, - analyses=self._analyze( - requests=self._requests, - cached_analyses=self._cached, - column_types_by_table=self._available_types, - column_nullability_by_table=self._available_nullability, - ), - complete_binding_schemas=self._complete_shapes, - ) - - def _analyze_ready(self, ready: tuple[str, ...]) -> None: - wave: list[ModelSqlAnalysisRequest] = [] - for name in ready: - request: ModelSqlAnalysisRequest = self._by_name[name] - if any(parent in self._changed for parent in self._dependencies[name]): - self._changed.add(name) - if request.cache_key is not None: - self._reusable.pop(request.cache_key, None) - bindings: dict[str, dict[str, str]] | None = ( - None - if request.binding_schema is None - else { - relation: self._complete_shapes.get(relation, {}) - for relation in binding_relation_names(request.model_input.references) - } - ) - wave.append(replace(request, binding_schema=bindings)) - wave_requests: tuple[ModelSqlAnalysisRequest, ...] = tuple(wave) - wave_names: set[str] = set(ready) - for request in wave_requests: - wave_names.update(binding_relation_names(request.model_input.references)) - analyses: tuple[ModelSqlAnalysis, ...] = self._complete( - requests=wave_requests, - analyses=self._analyze( - requests=wave_requests, - cached_analyses=self._reusable, - column_types_by_table=self._available_types, - column_nullability_by_table=self._available_nullability, - ), - complete_binding_schemas={ - name: self._complete_shapes[name] - for name in wave_names - if name in self._complete_shapes - }, - supplied_relations=frozenset( - name - for name in wave_names - if name in self._complete_shapes - and self._available_types.get(name, {}).keys() == self._complete_shapes[name].keys() - ), - ) - for name, request, result in zip(ready, wave_requests, analyses, strict=True): - self._results[name] = result - self._publish(name=name, request=request, result=result) - - def _publish( - self, *, name: str, request: ModelSqlAnalysisRequest, result: ModelSqlAnalysis - ) -> None: - analysis: PolyglotAnalysisResult = result.polyglot_analysis - if model_analysis_output_signature(analysis) != self._previous_signatures.get(name): - self._changed.add(name) - required: frozenset[str] = binding_relation_names(request.model_input.references) - star_known: bool = not analysis.has_star or ( - analysis.star_resolved and required <= self._complete_shapes.keys() - ) - if not analysis.columns or not star_known: - return - shape: dict[str, str] = published_model_shape( - sql=result.cleaned_sql or request.query_sql, - columns={column.name: column.type or "UNKNOWN" for column in analysis.columns}, - inputs={table: self._complete_shapes.get(table, {}) for table in required}, - profile=self._profile, - config_values=request.model_input.config.values, - ) - self._complete_shapes.setdefault(name, shape) - self._available_types.setdefault(name, shape) - self._available_nullability.setdefault( - name, dict.fromkeys(shape, InferredNullability.UNKNOWN) - ) diff --git a/src/sqlbuild/compiler/compile/classes/python_model_analysis.py b/src/sqlbuild/compiler/compile/classes/python_model_analysis.py index 1e5231d406..311ff4dfde 100644 --- a/src/sqlbuild/compiler/compile/classes/python_model_analysis.py +++ b/src/sqlbuild/compiler/compile/classes/python_model_analysis.py @@ -1,4 +1,4 @@ -"""Python's half of native model analysis: per-model request rows and deferred analyses.""" +"""Python's half of native model analysis: per-model request rows and cache metrics.""" from __future__ import annotations @@ -8,9 +8,6 @@ substitute_placeholder_defaults, table_function_analysis_name, ) -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, -) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import names_set_operation from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( binding_relation_names, @@ -24,22 +21,18 @@ CompileModelInput, CompileSqlReference, ModelSqlAnalysisRequest, - NativeCompactAnalysis, - PolyglotAnalysisResult, ) -from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.planner.constants import ( SNAPSHOT_DEFAULT_VALID_FROM_COLUMN, SNAPSHOT_DEFAULT_VALID_TO_COLUMN, ) from sqlbuild.compiler.planner.types import ContractPolicy, MaterializationType from sqlbuild.compiler.references.types import SqlReferenceKind -from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily class PythonModelAnalysis: - """The analysed models' request facts, and today's Python analysis where native defers.""" + """The analysed models' request facts.""" def __init__( self, @@ -70,51 +63,6 @@ def model_rows(self) -> list[tuple[object, ...]]: return [_model_row(request) for request in self.requests] - def analyze_deferred( - self, - *, - model: int, - precomputed: NativeCompactAnalysis, - binding_schema: dict[str, dict[str, str]], - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - ) -> PolyglotAnalysisResult: - """Python's legacy analysis of a model the native engine handed back.""" - - request: ModelSqlAnalysisRequest = self.requests[model] - return analyze_columns_and_lineage_with_polyglot( - query_sql=request.query_sql, - references=request.model_input.references, - placeholders=request.placeholders, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=self._profile, - allow_compact_analysis=True, - binding_schema=binding_schema, - recover_cte_facts=_recovers_cte_facts(request.model_input), - precomputed=precomputed, - ) - - def enrich( - self, *, model: int, input_schemas: dict[str, dict[str, str]] - ) -> PolyglotAnalysisResult: - """Python's re-analysis of a model with its known input shapes.""" - - request: ModelSqlAnalysisRequest = self.requests[model] - return analyze_columns_and_lineage_with_polyglot( - query_sql=request.query_sql, - references=request.model_input.references, - placeholders=request.placeholders, - column_types_by_table=input_schemas, - column_nullability_by_table={ - table: dict.fromkeys(columns, InferredNullability.UNKNOWN) - for table, columns in input_schemas.items() - }, - inference_profile=self._profile, - allow_compact_analysis=True, - recover_cte_facts=_recovers_cte_facts(request.model_input), - ) - @staticmethod def family_row(family: SchemaDynamicColumnFamily) -> tuple[str, str, str, str, str, str | None]: """One declared dynamic column family as the native request row.""" @@ -128,44 +76,6 @@ def family_row(family: SchemaDynamicColumnFamily) -> tuple[str, str, str, str, s family.name_pattern, ) - @staticmethod - def legacy_precomputed( - *, - cleaned_sql: str, - binding_diagnostics: tuple[SqlBindingDiagnostic, ...], - lineage: list[tuple[str, int, int, list[tuple[str, str, str]]]] | None, - ) -> NativeCompactAnalysis: - """The native result Python's legacy analysis reads, with lineage rows when kept.""" - - if lineage is None: - return NativeCompactAnalysis( - cleaned_sql=cleaned_sql, - analysis=None, - projected=False, - binding_diagnostics=binding_diagnostics, - ) - pool: dict[str, int] = {} - facts: list[object] = [] - for output_column, transform_code, confidence_code, sources in lineage: - name_index: int = pool.setdefault(output_column, len(pool)) - source_indexes: list[list[int]] = [] - for source in sources: - source_indexes.append([pool.setdefault(value, len(pool)) for value in source]) - facts.append([name_index, None, 0, transform_code, confidence_code, source_indexes]) - return NativeCompactAnalysis( - cleaned_sql=cleaned_sql, - analysis=None, - projected=False, - binding_diagnostics=binding_diagnostics, - compact_rows=list(range(len(facts))), - compact_fact_rows=facts, - string_pool=tuple(pool), - compact_column_cache={}, - compact_template_index=0, - compact_fact_cache={}, - compact_decoded_fact_cache={}, - ) - def record_cached(self, *, hits: int, misses: int) -> None: """Record the analysis cache entries a native session read and missed.""" diff --git a/src/sqlbuild/compiler/compile/models.py b/src/sqlbuild/compiler/compile/models.py index b350f2d3de..86ecc786ca 100644 --- a/src/sqlbuild/compiler/compile/models.py +++ b/src/sqlbuild/compiler/compile/models.py @@ -1488,34 +1488,6 @@ class PreparedBindingQuery: shared: SharedBindingQuery | None = None -@dataclass(frozen=True) -class CompactAnalysisCacheModel: - """One ordered model identity and its project-model dependencies.""" - - name: str - cache_key: str | None - upstream_names: tuple[str, ...] - - -@dataclass(frozen=True) -class CompactAnalysisCachePlan: - """Current invocation identity for one compact analysis generation.""" - - context: AnalysisCacheContext - batch_key: str - cache_keys: tuple[str, ...] - reuse_keys: tuple[str, ...] - - -@dataclass(frozen=True) -class CompactAnalysisCacheCandidate: - """Validated persisted generation and positions safe for reuse.""" - - matching_indexes: tuple[int, ...] - preparation: CompactBatchPreparation - response: object - - @dataclass(frozen=True) class CompactProjectionCaches: """Shared identity caches used while decoding compact native facts.""" diff --git a/tests/conftest.py b/tests/conftest.py index b0fd51dc67..d62b6a27d3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,9 +18,7 @@ def pytest_configure(config: pytest.Config) -> None: @pytest.fixture -def deferred_native_analysis(monkeypatch: pytest.MonkeyPatch) -> None: - """Make native model analysis and expression-source shapes defer, so compiles run the Python - analysis that answers native deferrals.""" +def deferred_native_expression_shapes(monkeypatch: pytest.MonkeyPatch) -> None: + """Make native expression-source shapes defer, so compiles run the Python shape inference.""" - monkeypatch.setattr(native_assembly, "analyze_native_model_sql", lambda **_: None) monkeypatch.setattr(native_assembly, "infer_native_expression_source_shapes", lambda **_: None) diff --git a/tests/e2e/scripts/compiler_differential/_test_types.py b/tests/e2e/scripts/compiler_differential/_test_types.py index 0369dcd081..fd86575f11 100644 --- a/tests/e2e/scripts/compiler_differential/_test_types.py +++ b/tests/e2e/scripts/compiler_differential/_test_types.py @@ -19,19 +19,6 @@ class HarnessRunTestCase: expected_absent: tuple[str, ...] -@dataclass(frozen=True) -class SharedAnalysisSeedTestCase: - """A generated seed that must compile cleanly and reuse shared analyses.""" - - description: str - seed: int - expected_lines: tuple[str, ...] - expected_capture_sides: tuple[str, ...] - expected_compile_exit_code: int - expected_minimum_shareable_members: int - expected_minimum_shared_reuse: int - - @dataclass(frozen=True) class ProjectExpectationTestCase: """A broken project run with one --expect value and the verdict the harness must reach.""" diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 2110dc67b6..8bf73b344c 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -175,28 +175,3 @@ def perturbation_arguments(directory: Path, *, source: str = _SITECUSTOMIZE) -> "--engine-env", f"native-preview:PYTHONPATH={write_native_perturbation(directory, source=source)}", ) - - -SHARED_ANALYSIS_SEED: int = 34 -COMPILED_PROJECT_CAPTURE: str = "0-compile/003-compiled_project.json" - - -def shared_analysis_seed_arguments(*, work_dir: Path, seed: int) -> list[str]: - """Harness arguments comparing one generated seed with itself, capturing every stage.""" - - return [ - "--jobs", - "1", - "--work-dir", - str(work_dir), - "--corpus", - "seeds", - "--seed-start", - str(seed), - "--seeds", - "1", - "--engines", - "native", - "native", - "--stage-captures", - ] diff --git a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py index 4d95fa8795..d0a0ba685a 100644 --- a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py +++ b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py @@ -4,7 +4,6 @@ import json import re -import shutil from pathlib import Path import pytest @@ -14,28 +13,19 @@ CoverageFailureTestCase, HarnessRunTestCase, ProjectExpectationTestCase, - SharedAnalysisSeedTestCase, WheelSiteReportTestCase, ) from tests.e2e.scripts.compiler_differential.helpers import ( CATALOG_PERTURBATION, - COMPILED_PROJECT_CAPTURE, DEFERRAL_PERTURBATION, DISCOVERY_PERTURBATION, NATIVE_ONLY_STDERR_LINE, RENDER_PERTURBATION, - SHARED_ANALYSIS_SEED, harness_arguments, perturbation_arguments, - shared_analysis_seed_arguments, write_broken_ref_project, write_failure_case_project, ) -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - CompiledProjectRun, - compiled_project_capture, - use_wave_analysis, -) @pytest.mark.parametrize( @@ -338,62 +328,6 @@ def test_given_required_coverage_missing_when_comparing_then_harness_fails_and_s assert not any(text in output for text in test_case.expected_absent), output -@pytest.mark.parametrize( - "test_case", - [ - SharedAnalysisSeedTestCase( - description="shared_analysis_seed_against_itself", - seed=SHARED_ANALYSIS_SEED, - expected_lines=( - f"OK seed/{SHARED_ANALYSIS_SEED}", - "Compiler differential passed: 3 projects identical (native vs native)", - ), - expected_capture_sides=("left-native-captures", "right-native-captures"), - expected_compile_exit_code=0, - expected_minimum_shareable_members=1, - expected_minimum_shared_reuse=1, - ) - ], - ids=lambda case: case.description, -) -@pytest.mark.usefixtures("deferred_native_analysis") -def test_given_native_engine_twice_when_capturing_stages_then_captures_are_identical( - test_case: SharedAnalysisSeedTestCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - work_dir: Path = tmp_path / "work" - exit_code: int = run_compiler_differential( - shared_analysis_seed_arguments(work_dir=work_dir, seed=test_case.seed) - ) - output: str = capsys.readouterr().out - seed_dir: Path = work_dir / f"seed__{test_case.seed}" - project_dir: Path = Path(shutil.copytree(seed_dir / "source", tmp_path / "project")) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - compiled: CompiledProjectRun = compiled_project_capture( - project_dir=project_dir, - capture_dir=tmp_path / "captures", - capsys=capsys, - monkeypatch=waves_patch, - ) - - assert (exit_code, all(line in output for line in test_case.expected_lines)) == (0, True), ( - output - ) - assert "DIFF" not in output, output - assert all( - (seed_dir / side / COMPILED_PROJECT_CAPTURE).is_file() - for side in test_case.expected_capture_sides - ), output - assert ( - compiled.exit_code, - compiled.shareable_members >= test_case.expected_minimum_shareable_members, - compiled.shared_reuse >= test_case.expected_minimum_shared_reuse, - ) == (test_case.expected_compile_exit_code, True, True), compiled - - @pytest.mark.parametrize( "test_case", [ diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py index 9099ad17ef..3474593727 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py @@ -792,13 +792,6 @@ class RetiredCompilerCacheTestCase: expected_removed: bool -@dataclass(frozen=True) -class DenseBatchedPreparationTestCase: - description: str - model_count: int - expected_exit_code: int - - type DensePreparedCompile = PreparedCompile diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py index 3cdb726d46..e8efafe438 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py @@ -35,7 +35,6 @@ import sqlbuild.cli.compile_reuse._helpers.attempt as reuse_attempt import sqlbuild.cli.compile_reuse._helpers.project_files as reuse_project_files import sqlbuild.cli.compile_reuse._helpers.store as reuse_store -import sqlbuild.compiler.compile._helpers.assembly.binding_waves as binding_waves import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.diagnostics.recovery as diagnostic_recovery import sqlbuild.compiler.compile._helpers.macro_bridge.call_store as call_store_module @@ -3881,13 +3880,6 @@ def twin_header_changed(root: Path) -> None: ) -def analyze_one_model_at_a_time(monkeypatch: pytest.MonkeyPatch) -> None: - """Run the binding dataflow with one worker taking one model at a time.""" - - for name in ("_DATAFLOW_WORKERS", "_DATAFLOW_BATCH_MIN", "_DATAFLOW_BATCH_LIMIT"): - monkeypatch.setattr(binding_waves, name, 1) - - def analyze_in_one_batch(monkeypatch: pytest.MonkeyPatch) -> None: """Analyze every model in one batch, without dependency-ordered binding.""" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py index 9c98cf36d3..a02459ad51 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py @@ -22,7 +22,6 @@ adapter_switched, add_external_flavor_macro, analyze_in_one_batch, - analyze_one_model_at_a_time, build_between, change_adapter_lexical_rules, compare_incremental_compile, @@ -420,9 +419,6 @@ def test_given_broken_cache_invalidation_when_compiling_an_edit_then_the_oracle_ @pytest.mark.parametrize( "test_case", [ - SharedCacheKeyTestCase( - description="dataflow_one_model_at_a_time", schedule=analyze_one_model_at_a_time - ), SharedCacheKeyTestCase(description="one_batch", schedule=analyze_in_one_batch), ], ids=lambda case: case.description, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_batched_preparation.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_batched_preparation.py deleted file mode 100644 index fffac8f29e..0000000000 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_batched_preparation.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Dense batched analysis preparation must match per-model preparation byte for byte.""" - -from pathlib import Path - -import pytest - -from scripts.cold_compile_performance._helpers.dense_project import write_dense_compile_project -from sqlbuild.compiler.compile.constants import COMPACT_RELATION_STUB_PREFIX -from tests.e2e.src.sqlbuild.cli.commands.main.compile._test_types import ( - DenseBatchedPreparationTestCase, - DensePreparedCompile, -) -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - keep_batched_normalization, - prepared_compile, - use_per_model_normalization, -) - - -@pytest.mark.performance -@pytest.mark.parametrize( - "test_case", - (DenseBatchedPreparationTestCase("dense_models_3000", 3000, 0),), - ids=lambda case: case.description, -) -def test_given_dense_project_when_compiling_cold_then_batched_preparation_matches_per_model( - test_case: DenseBatchedPreparationTestCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - batched_dir: Path = tmp_path / "batched" - reference_dir: Path = tmp_path / "reference" - write_dense_compile_project(project_dir=batched_dir, model_count=test_case.model_count) - write_dense_compile_project(project_dir=reference_dir, model_count=test_case.model_count) - - expected: DensePreparedCompile = prepared_compile( - project_dir=reference_dir, - args=(), - capsys=capsys, - monkeypatch=monkeypatch, - normalization=use_per_model_normalization, - ) - actual: DensePreparedCompile = prepared_compile( - project_dir=batched_dir, - args=(), - capsys=capsys, - monkeypatch=monkeypatch, - normalization=keep_batched_normalization, - ) - - assert actual[3] == expected[3] - assert actual[1] == expected[1] - assert actual[2] == expected[2] - assert actual[0] == expected[0] - assert actual[0][0] == test_case.expected_exit_code - assert any(COMPACT_RELATION_STUB_PREFIX.encode() in payload for payload in actual[3]) - - -if __name__ == "__main__": - raise SystemExit(pytest.main([__file__, "-vv"])) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 18a19223ca..dbb3390ebe 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -7,8 +7,8 @@ @dataclass(frozen=True) -class GeneratedAnalysisParityTestCase: - """Seeded generated projects whose model analysis both engines must agree on.""" +class GeneratedAnalysisTestCase: + """Seeded generated projects whose uncached, cached and re-proven native analyses agree.""" description: str seed: int @@ -20,23 +20,17 @@ class GeneratedAnalysisParityTestCase: expected_minimum_native: int expected_minimum_expression_shapes: int expected_minimum_pivot_proofs: int - expected_minimum_python_cte_recoveries: int - expected_minimum_legacy_analyses: int - expected_legacy_analysis_deferrals: int expected_minimum_proven_pivots: int - expected_maximum_enrichment_deferrals: int expected_minimum_native_enrichments: int @dataclass(frozen=True) -class AnalysisFallbackTestCase: - """A project the native session hands back to Python whole, recording why.""" +class InternalFailureTestCase: + """A project whose native analysis fails internally, and the error the compile raises.""" description: str files: dict[str, str] - allow_compact_analysis: bool - keeps_catalog: bool - expected_kind: str + expected_message: str @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index c22c15a1a7..5a37b7dd59 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -6,7 +6,6 @@ from collections.abc import Callable from contextlib import suppress from dataclasses import dataclass, field, replace -from functools import partial from itertools import chain from pathlib import Path from typing import Any, cast @@ -15,7 +14,6 @@ import sqlbuild._native as native_module import sqlbuild.compiler.analysis_session.classes.native_model_analysis as native_model_analysis -import sqlbuild.compiler.compile._helpers.analysis.compact as compact_analysis import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stage_assembly from sqlbuild.adapter.contract.models import ExpressionInferenceProfile @@ -35,12 +33,23 @@ NativeModelAnalysisRequest, NativePivotTables, ) +from sqlbuild.compiler.compile._helpers.analysis.cache import build_analysis_cache_context +from sqlbuild.compiler.compile._helpers.analysis.pivot_requests import ( + model_dynamic_families, + model_pivot_sql, +) +from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_placeholders from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( get_expression_source_shapes, ) +from sqlbuild.compiler.compile._helpers.render.cursor_intrinsics import ( + cursor_intrinsics_analysis_sql, +) +from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( + AnalysisCacheContext, CompactLineageFacts, CompileAdapterContext, CompiledLineageColumnFact, @@ -230,11 +239,6 @@ def _pivot_models(rng: random.Random) -> dict[str, str]: return files -_PYTHON_CTE_RECOVERY: Callable[..., Any] = compact_analysis._polyglot_cte_passthrough_facts -_PYTHON_LEGACY_ANALYSIS: Callable[..., Any] = ( - compact_analysis._analyze_columns_and_lineage_from_polyglot_ast -) - type _Relation = tuple[str, tuple[str, ...]] type _Template = Callable[[random.Random, list[_Relation]], tuple[str, str, tuple[str, ...]]] @@ -412,7 +416,7 @@ class NativePivotProofs: def native_pivot_proofs(*, monkeypatch: pytest.MonkeyPatch) -> NativePivotProofs: - """Record every batch of native pivot proofs assembly takes, failing any wheel pivot proof.""" + """Record every batch of native pivot proofs assembly takes.""" recorded_proofs: NativePivotProofs = NativePivotProofs() native_proofs: Callable[..., tuple[DynamicColumnContractProof | None, ...]] = ( @@ -426,16 +430,7 @@ def recorded(**keywords: Any) -> tuple[DynamicColumnContractProof | None, ...]: recorded_proofs.session_proofs += proven * (keywords["session"] is not None) return proofs - python_proof: Callable[..., DynamicColumnContractProof | None] = ( - project_assembly.analyze_dynamic_column_contract - ) - - def wheel_not_expected(**keywords: Any) -> DynamicColumnContractProof | None: - assert not keywords["families"], "the Python wheel proved a pivot native should prove" - return python_proof(**keywords) - monkeypatch.setattr(native_stage_assembly, "prove_native_dynamic_contracts", recorded) - monkeypatch.setattr(project_assembly, "analyze_dynamic_column_contract", wheel_not_expected) return recorded_proofs @@ -560,32 +555,16 @@ def compiled_project_view( ) -def python_compiled_project_view( - *, - project_dir: Path, - files: dict[str, str], - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> tuple[int, object, object, dict[str, str]]: - """`compiled_project_view` with native model analysis deferred, so Python analyses all.""" - - with monkeypatch.context() as patch: - patch.setattr(native_stage_assembly, "analyze_native_model_sql", lambda **_kwargs: None) - return compiled_project_view(project_dir=project_dir, files=files, capsys=capsys) - - @dataclass -class AnalysisParity: - """Python's and the native session's views of every analysis seam call.""" +class NativeAnalysisRuns: + """Each native analysis seam call's uncached, cold-cached and warm-cached views.""" names: list[object] = field(default_factory=list) - python: list[object] = field(default_factory=list) - native: list[object] = field(default_factory=list) + uncached: list[object] = field(default_factory=list) + cached: list[object] = field(default_factory=list) analysed_models: int = 0 expression_shapes: int = 0 pivot_proofs: int = 0 - python_cte_recoveries: int = 0 - legacy_analyses: int = 0 standalone_proofs: int = 0 session_proofs: int = 0 proven_pivots: int = 0 @@ -594,89 +573,55 @@ class AnalysisParity: native_column_values: int = 0 -def compare_analyses( +def analyse_natively( *, inputs: CompileProjectInputs, inference_profile: ExpressionInferenceProfile, lineage_mode: ColumnLineageMode, - parity: AnalysisParity, + runs: NativeAnalysisRuns, + cache_root: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - """Assemble `inputs` under `inference_profile`, analysing every seam call with both engines.""" + """Assemble `inputs`, analysing each model batch natively uncached, cold-cached and warm. - sessions: list[Any] = [] + Every analysis, catalog change, session and standalone dynamic pivot proof, and expression + shape must not depend on the analysis store, on the session, or on Python's shape inference. + """ - def models_by_both( - *, - python_analysis: partial[dict[str, ModelSqlAnalysis]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], + original_analysis: Callable[..., tuple[dict[str, ModelSqlAnalysis], Any]] = ( + native_stage_assembly.analyze_model_sql + ) + + def analysed_three_ways( + request: NativeModelAnalysisRequest, ) -> tuple[dict[str, ModelSqlAnalysis], Any]: - keywords: dict[str, Any] = {**python_analysis.keywords, "analysis_cache": None} - profile: ExpressionInferenceProfile = keywords["inference_profile"] - native_catalog: Any = profile.binding_catalog.with_relations({}) - native_result: NativeModelAnalyses | None = analyze_native_model_sql( - request=NativeModelAnalysisRequest( - **{ - **keywords, - "inference_profile": replace(profile, binding_catalog=native_catalog), - }, - dynamic_families_by_table=dynamic_families_by_table, - ) + profile: ExpressionInferenceProfile = request.inference_profile + cache: AnalysisCacheContext | None = build_analysis_cache_context( + root=cache_root, inference_profile=profile, allow_compact_analysis=True ) - native: dict[str, ModelSqlAnalysis] | None = getattr(native_result, "analyses", None) - sessions.append(getattr(native_result, "session", None)) - with monkeypatch.context() as patch: - patch.setattr( - compact_analysis, - "_polyglot_cte_passthrough_facts", - partial(_counted_cte_recovery, parity=parity), - ) - patch.setattr( - compact_analysis, - "_analyze_columns_and_lineage_from_polyglot_ast", - partial(_counted_legacy_analysis, parity=parity), + cached_views: list[object] = [] + for _ in range(2): + catalog: Any = profile.binding_catalog.with_relations({}) + cached: NativeModelAnalyses = analyze_native_model_sql( + request=replace( + request, + analysis_cache=cache, + inference_profile=replace(profile, binding_catalog=catalog), + ) ) - python: dict[str, ModelSqlAnalysis] = python_analysis.func(**keywords) + cached_views.append((_analysis_views(cached.analyses), _catalog_view(catalog))) + analyses, session = original_analysis(replace(request, analysis_cache=None)) + uncached_view: object = (_analysis_views(analyses), _catalog_view(profile.binding_catalog)) columns: list[InferredColumn] = list( - chain.from_iterable(map(_native_columns, (native or {}).values())) + chain.from_iterable(map(_native_columns, analyses.values())) ) - parity.native_column_objects += len(set(map(id, columns))) - parity.native_column_values += len(set(columns)) - parity.analysed_models += len(python) - _append(parity, "model analyses", _analysis_views(python), _analysis_views(native)) - _append( - parity, - "catalog", - _catalog_view(profile.binding_catalog), - _catalog_view(native_catalog), - ) - return native or python, sessions[-1] - - def proofs_by_both( - *, - sql_analysis: ModelSqlAnalysis | None, - native_proof: DynamicColumnContractProof | None, - python_proof: partial[DynamicColumnContractProof | None], - ) -> DynamicColumnContractProof | None: - native: DynamicColumnContractProof | None = getattr( - sql_analysis, "dynamic_column_contract", None - ) - python: DynamicColumnContractProof | None = python_proof() - session: Any = (sessions or [None])[-1] - standalone: DynamicColumnContractProof | None = _batched_proof( - session=None, python_proof=python_proof - ) - in_session: DynamicColumnContractProof | None = _batched_proof( - session=session, python_proof=python_proof - ) - parity.session_proofs += in_session is not None and session is not None - parity.pivot_proofs += native is not None - parity.proven_pivots += native is not None and native.output_proven - parity.standalone_proofs += standalone is not None - _append(parity, "dynamic pivot proof", python, native or python) - _append(parity, "standalone dynamic pivot proof", python, standalone or python) - _append(parity, "session dynamic pivot proof", python, in_session or python) - return python + runs.native_column_objects += len(set(map(id, columns))) + runs.native_column_values += len(set(columns)) + runs.analysed_models += len(analyses) + for index, view in enumerate(cached_views): + _append(runs, f"model analyses, cached run {index}", uncached_view, view) + _compare_proofs(request=request, analyses=analyses, session=session, runs=runs) + return analyses, session def shapes_by_both( *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile @@ -689,9 +634,9 @@ def shapes_by_both( python: tuple[dict[str, str] | None, ...] = get_expression_source_shapes( expressions=expressions, profile=profile ) - parity.expression_shapes += sum(shape is not None for shape in python) + runs.expression_shapes += sum(shape is not None for shape in python) _append( - parity, + runs, "expression shapes", (python, catalog.expression_shapes), (native, native_catalog.expression_shapes), @@ -703,14 +648,13 @@ def shapes_by_both( ) def counted_native_enrichment(rows: list[Any]) -> tuple[CompiledLineageColumnFact, ...]: - parity.native_enrichments += 1 + runs.native_enrichments += 1 return native_lineage_facts(rows) with monkeypatch.context() as patch: patch.setattr(native_model_analysis, "lineage_facts", counted_native_enrichment) - patch.setattr(project_assembly, "analyze_model_sql_by_engine", models_by_both) + patch.setattr(project_assembly, "analyze_model_sql", analysed_three_ways) patch.setattr(project_assembly, "expression_source_shapes_by_engine", shapes_by_both) - patch.setattr(project_assembly, "dynamic_column_contract_by_engine", proofs_by_both) with suppress(CompileInputError): _ = project_assembly.assemble_compiled_project( inputs=inputs, @@ -719,8 +663,36 @@ def counted_native_enrichment(rows: list[Any]) -> tuple[CompiledLineageColumnFac ) +def record_python_model_analyses(*, monkeypatch: pytest.MonkeyPatch) -> list[object]: + """Record every Python model analysis assembly runs; return the list they are appended to.""" + + calls: list[object] = [] + original: Callable[..., Any] = project_assembly.analyze_columns_and_lineage_with_polyglot + + def recorded(**keywords: Any) -> Any: + calls.append(keywords.get("query_sql")) + return original(**keywords) + + monkeypatch.setattr(project_assembly, "analyze_columns_and_lineage_with_polyglot", recorded) + return calls + + +def duplicate_analysed_model_names(*, monkeypatch: pytest.MonkeyPatch) -> None: + """Name every analysed model after the first, an invariant discovery guarantees (D007).""" + + original: Callable[[PythonModelAnalysis], list[tuple[object, ...]]] = ( + PythonModelAnalysis.model_rows + ) + + def renamed(analysis: PythonModelAnalysis) -> list[tuple[object, ...]]: + rows: list[tuple[object, ...]] = original(analysis) + return [(rows[0][0], *row[1:]) for row in rows] + + monkeypatch.setattr(PythonModelAnalysis, "model_rows", renamed) + + def custom_nullability_rule(arguments: tuple[InferredNullability, ...]) -> InferredNullability: - """An adapter rule SQLBuild does not ship, so native analysis leaves it to Python.""" + """An adapter rule SQLBuild does not ship, so native analysis calls it back.""" return InferredNullability.NULLABLE @@ -735,20 +707,12 @@ def analysis_request( requests: list[NativeModelAnalysisRequest] = [] - def captured( - *, - python_analysis: partial[dict[str, ModelSqlAnalysis]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], - ) -> tuple[dict[str, ModelSqlAnalysis], None]: - requests.append( - NativeModelAnalysisRequest( - **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table - ) - ) - return python_analysis(), None + def captured(request: NativeModelAnalysisRequest) -> tuple[dict[str, ModelSqlAnalysis], None]: + requests.append(request) + return {}, None with monkeypatch.context() as patch, suppress(CompileInputError): - patch.setattr(project_assembly, "analyze_model_sql_by_engine", captured) + patch.setattr(project_assembly, "analyze_model_sql", captured) _ = project_assembly.assemble_compiled_project( inputs=inputs, inference_profile=inference_profile, @@ -773,42 +737,62 @@ def _native_columns(analysis: ModelSqlAnalysis) -> tuple[InferredColumn, ...]: return analysis.polyglot_analysis.columns or () -def _counted_legacy_analysis(*, parity: AnalysisParity, **arguments: Any) -> Any: - parity.legacy_analyses += 1 - return _PYTHON_LEGACY_ANALYSIS(**arguments) - - -def _counted_cte_recovery(*, parity: AnalysisParity, **arguments: Any) -> Any: - recovered: Any = _PYTHON_CTE_RECOVERY(**arguments) - parity.python_cte_recoveries += bool(recovered[2]) - return recovered - - -def _batched_proof( - *, session: Any, python_proof: partial[DynamicColumnContractProof | None] -) -> DynamicColumnContractProof | None: - keywords: dict[str, Any] = python_proof.keywords - return prove_native_dynamic_contracts( - session=session, - tables=NativePivotTables( - dialect=keywords["dialect"], - column_types_by_table=keywords["column_types_by_table"], - authoritative_column_types_by_table=keywords["authoritative_column_types_by_table"], - column_nullability_by_table=keywords["column_nullability_by_table"], - dynamic_families_by_table=keywords["dynamic_families_by_table"], - ), - models=((keywords["query_sql"], keywords["families"]),), - )[0] - - -def _append(parity: AnalysisParity, name: str, python: object, native: object) -> None: - parity.names.append(name) - parity.python.append(python) - parity.native.append(native) - - -def _analysis_views(analyses: dict[str, ModelSqlAnalysis] | None) -> object: - return analyses and { +def _compare_proofs( + *, + request: NativeModelAnalysisRequest, + analyses: dict[str, ModelSqlAnalysis], + session: Any, + runs: NativeAnalysisRuns, +) -> None: + """Each analysed pivot model's proof, proven again in the session and standalone.""" + + tables: NativePivotTables = NativePivotTables( + dialect=request.inference_profile.sql_analysis_dialect, + column_types_by_table=request.column_types_by_table, + authoritative_column_types_by_table=request.complete_binding_schemas, + column_nullability_by_table=request.column_nullability_by_table, + dynamic_families_by_table=request.dynamic_families_by_table, + ) + for model_input in request.model_inputs: + families: tuple[SchemaDynamicColumnFamily, ...] = model_dynamic_families(model_input) + analysis: ModelSqlAnalysis | None = analyses.get(model_input.model_file.file_path.stem) + if not families or analysis is None: + continue + models: tuple[tuple[str, tuple[SchemaDynamicColumnFamily, ...]], ...] = ( + ( + model_pivot_sql( + query_sql=cursor_intrinsics_analysis_sql( + sql=model_input.query_sql, + cursor_type=model_input.config.values.get("cursor_type"), + ), + placeholders=model_placeholders(model_input), + ), + families, + ), + ) + proof: DynamicColumnContractProof | None = analysis.dynamic_column_contract + standalone: DynamicColumnContractProof | None = prove_native_dynamic_contracts( + session=None, tables=tables, models=models + )[0] + in_session: DynamicColumnContractProof | None = prove_native_dynamic_contracts( + session=session, tables=tables, models=models + )[0] + runs.pivot_proofs += proof is not None + runs.proven_pivots += proof is not None and proof.output_proven + runs.standalone_proofs += standalone is not None + runs.session_proofs += in_session is not None + _append(runs, "standalone dynamic pivot proof", proof, standalone) + _append(runs, "session dynamic pivot proof", proof, in_session) + + +def _append(runs: NativeAnalysisRuns, name: str, uncached: object, cached: object) -> None: + runs.names.append(name) + runs.uncached.append(uncached) + runs.cached.append(cached) + + +def _analysis_views(analyses: dict[str, ModelSqlAnalysis]) -> object: + return { name: ( analysis.polyglot_analysis.analysis_succeeded, analysis.polyglot_analysis.columns, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 44436feba6..fbb25be238 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -4,13 +4,13 @@ import random from collections import Counter -from dataclasses import replace from itertools import product from pathlib import Path from typing import Any import pytest +import sqlbuild._native as native_module from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter @@ -29,27 +29,28 @@ from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( AdapterRuleCallbackTestCase, - AnalysisFallbackTestCase, CyclicAnalysisTestCase, CyclicCompileTestCase, - GeneratedAnalysisParityTestCase, + GeneratedAnalysisTestCase, + InternalFailureTestCase, SharedAnalysisTestCase, StandalonePivotProofTestCase, ) from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( ADAPTER_RULE_MODELS, - AnalysisParity, + NativeAnalysisRuns, NativePivotProofs, + analyse_natively, analysis_request, - compare_analyses, compile_inputs, compiled_project_view, custom_nullability_rule, deferral_kinds, + duplicate_analysed_model_names, generated_analysis_files, native_pivot_proofs, pivot_project_files, - python_compiled_project_view, + record_python_model_analyses, shared_analysis_files, started_sessions, ) @@ -107,7 +108,7 @@ @pytest.mark.parametrize( "test_case", [ - GeneratedAnalysisParityTestCase( + GeneratedAnalysisTestCase( description="stars, CTEs, CTE facts, set operations, untyped inputs, contracts, pivots", seed=20261008, count=6, @@ -118,14 +119,10 @@ expected_minimum_native=500, expected_minimum_expression_shapes=40, expected_minimum_pivot_proofs=80, - expected_minimum_python_cte_recoveries=39, - expected_minimum_legacy_analyses=152, - expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=12, - expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=90, ), - GeneratedAnalysisParityTestCase( + GeneratedAnalysisTestCase( description="rich lineage over untyped inputs, CTE facts and contracts", seed=20261008, count=6, @@ -136,14 +133,10 @@ expected_minimum_native=500, expected_minimum_expression_shapes=40, expected_minimum_pivot_proofs=80, - expected_minimum_python_cte_recoveries=27, - expected_minimum_legacy_analyses=95, - expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=12, - expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=90, ), - GeneratedAnalysisParityTestCase( + GeneratedAnalysisTestCase( description="snowflake and duckdb adapter rules, fast lineage", seed=20261010, count=3, @@ -154,14 +147,10 @@ expected_minimum_native=200, expected_minimum_expression_shapes=15, expected_minimum_pivot_proofs=30, - expected_minimum_python_cte_recoveries=4, - expected_minimum_legacy_analyses=52, - expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=10, - expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=30, ), - GeneratedAnalysisParityTestCase( + GeneratedAnalysisTestCase( description="snowflake and duckdb adapter rules, rich lineage", seed=20261010, count=3, @@ -172,14 +161,10 @@ expected_minimum_native=190, expected_minimum_expression_shapes=15, expected_minimum_pivot_proofs=30, - expected_minimum_python_cte_recoveries=4, - expected_minimum_legacy_analyses=62, - expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=10, - expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=20, ), - GeneratedAnalysisParityTestCase( + GeneratedAnalysisTestCase( description="a project-local adapter's own nullability rules, called back natively", seed=20261011, count=3, @@ -190,25 +175,21 @@ expected_minimum_native=1, expected_minimum_expression_shapes=1, expected_minimum_pivot_proofs=1, - expected_minimum_python_cte_recoveries=0, - expected_minimum_legacy_analyses=1, - expected_legacy_analysis_deferrals=0, expected_minimum_proven_pivots=1, - expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=1, ), ], ids=lambda case: case.description, ) -def test_given_generated_projects_when_analysing_natively_then_matches_python( - test_case: GeneratedAnalysisParityTestCase, +def test_given_generated_projects_when_analysing_natively_then_cached_and_proven_runs_agree( + test_case: GeneratedAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: record_dir: Path = tmp_path / "records" monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) rng: random.Random = random.Random(test_case.seed) - parity: AnalysisParity = AnalysisParity() + runs: NativeAnalysisRuns = NativeAnalysisRuns() for index in range(test_case.count): inputs: CompileProjectInputs = compile_inputs( project_dir=tmp_path / f"project_{index}", @@ -217,39 +198,28 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( **test_case.extra_files, }, ) - for profile in test_case.inference_profiles: - compare_analyses( + for profile_index, profile in enumerate(test_case.inference_profiles): + analyse_natively( inputs=inputs, inference_profile=profile, lineage_mode=test_case.lineage_mode, - parity=parity, + runs=runs, + cache_root=tmp_path / f"cache_{index}_{profile_index}", monkeypatch=monkeypatch, ) kinds: Counter[str] = deferral_kinds(record_dir) + kinds.pop("analysis_session:adapter_nullability_callback", None) - assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] - assert kinds["analysis_session:session"] == kinds["analysis_session:expression_shapes"] == 0 - assert kinds["analysis_session:dynamic_pivot"] == 0 - assert kinds["analysis_session:legacy_analysis"] == test_case.expected_legacy_analysis_deferrals - assert parity.legacy_analyses >= test_case.expected_minimum_legacy_analyses - assert parity.python_cte_recoveries >= test_case.expected_minimum_python_cte_recoveries - assert ( - parity.analysed_models - - kinds["analysis_session:legacy_analysis"] - - kinds["analysis_session:input_enrichment"] - >= test_case.expected_minimum_native - ) - assert parity.expression_shapes >= test_case.expected_minimum_expression_shapes - assert parity.pivot_proofs >= test_case.expected_minimum_pivot_proofs - assert parity.standalone_proofs >= test_case.expected_minimum_pivot_proofs - assert parity.session_proofs >= test_case.expected_minimum_pivot_proofs - assert parity.proven_pivots >= test_case.expected_minimum_proven_pivots - assert ( - kinds["analysis_session:input_enrichment"] - <= test_case.expected_maximum_enrichment_deferrals - ) - assert parity.native_enrichments >= test_case.expected_minimum_native_enrichments - assert parity.native_column_objects == parity.native_column_values > 0 + assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] + assert kinds == Counter() + assert runs.analysed_models >= test_case.expected_minimum_native + assert runs.expression_shapes >= test_case.expected_minimum_expression_shapes + assert runs.pivot_proofs >= test_case.expected_minimum_pivot_proofs + assert runs.standalone_proofs >= test_case.expected_minimum_pivot_proofs + assert runs.session_proofs >= test_case.expected_minimum_pivot_proofs + assert runs.proven_pivots >= test_case.expected_minimum_proven_pivots + assert runs.native_enrichments >= test_case.expected_minimum_native_enrichments + assert runs.native_column_objects == runs.native_column_values > 0 @pytest.mark.parametrize( @@ -269,7 +239,7 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( ], ids=lambda case: case.description, ) -def test_given_equal_model_queries_when_analysing_natively_then_shares_and_matches_python( +def test_given_equal_model_queries_when_analysing_natively_then_shares_and_cached_runs_agree( test_case: SharedAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: record_dir: Path = tmp_path / "records" @@ -281,30 +251,32 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match ), ) sessions: list[Any] = started_sessions(monkeypatch=monkeypatch) - parity: AnalysisParity = AnalysisParity() + runs: NativeAnalysisRuns = NativeAnalysisRuns() _ = [ - compare_analyses( + analyse_natively( inputs=inputs, inference_profile=ExpressionInferenceProfile(sql_analysis_dialect=dialect), lineage_mode=ColumnLineageMode.FAST, - parity=parity, + runs=runs, + cache_root=tmp_path / f"cache_{dialect}", monkeypatch=monkeypatch, ) for dialect in test_case.dialects ] - shared: int = sum(session.sharing[0] for session in sessions) + uncached: list[Any] = sessions[2::3] + shared: int = sum(session.sharing[0] for session in uncached) - assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] + assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] assert ( - parity.analysed_models, + runs.analysed_models, shared, - sum(session.sharing[1] for session in sessions), - parity.analysed_models - shared, + sum(session.sharing[1] for session in uncached), + runs.analysed_models - shared, deferral_kinds(record_dir), - parity.native_column_objects, - parity.native_column_values, + runs.native_column_objects, + runs.native_column_values, ) == ( test_case.expected_analysed, test_case.expected_shared, @@ -329,7 +301,7 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match ], ids=lambda case: case.description, ) -def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_matches_python( +def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_cached_runs_agree( test_case: CyclicAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: record_dir: Path = tmp_path / "records" @@ -338,23 +310,24 @@ def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_ma project_dir=tmp_path / "project", files=test_case.files ) sessions: list[Any] = started_sessions(monkeypatch=monkeypatch) - parity: AnalysisParity = AnalysisParity() + runs: NativeAnalysisRuns = NativeAnalysisRuns() _ = [ - compare_analyses( + analyse_natively( inputs=inputs, inference_profile=ExpressionInferenceProfile(sql_analysis_dialect=dialect), lineage_mode=lineage_mode, - parity=parity, + runs=runs, + cache_root=tmp_path / f"cache_{dialect}_{lineage_mode.value}", monkeypatch=monkeypatch, ) for dialect, lineage_mode in product(test_case.dialects, test_case.lineage_modes) ] - assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] + assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] assert ( - parity.analysed_models, - len(sessions), + runs.analysed_models, + len(sessions) // 3, None in sessions, deferral_kinds(record_dir), ) == ( @@ -382,7 +355,7 @@ def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_ma ], ids=lambda case: case.description, ) -def test_given_cyclic_project_when_compiling_then_native_output_equals_python_analysis( +def test_given_cyclic_project_when_compiling_then_native_output_is_complete_and_repeatable( test_case: CyclicCompileTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str], @@ -396,14 +369,11 @@ def test_given_cyclic_project_when_compiling_then_native_output_equals_python_an ) native_deferrals: Counter[str] = deferral_kinds(record_dir) - python: tuple[int, object, object, dict[str, str]] = python_compiled_project_view( - project_dir=tmp_path / "python" / "orders_cycle", - files=test_case.files, - capsys=capsys, - monkeypatch=monkeypatch, + repeated: tuple[int, object, object, dict[str, str]] = compiled_project_view( + project_dir=tmp_path / "repeated" / "orders_cycle", files=test_case.files, capsys=capsys ) - assert native == python + assert native == repeated assert (native[0], tuple(native[3]), native_deferrals) == ( test_case.expected_exit_code, test_case.expected_compiled, @@ -414,46 +384,38 @@ def test_given_cyclic_project_when_compiling_then_native_output_equals_python_an @pytest.mark.parametrize( "test_case", [ - AnalysisFallbackTestCase( - description="lineage disabled, so compact analysis is off", + InternalFailureTestCase( + description="two analysed models sharing a name break the session's invariant", files=_ORDERS_PROJECT, - allow_compact_analysis=False, - keeps_catalog=True, - expected_kind="no_compact_analysis", - ), - AnalysisFallbackTestCase( - description="an inference profile without a binding catalog", - files=_ORDERS_PROJECT, - allow_compact_analysis=True, - keeps_catalog=False, - expected_kind="no_analysis_catalog", + expected_message=( + "NativeCompilerError: native model analysis: two analysed models share a name" + ), ), ], ids=lambda case: case.description, ) -def test_given_unsupported_analysis_when_analysing_natively_then_python_analyses_and_records( - test_case: AnalysisFallbackTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +def test_given_internal_native_failure_when_assembling_then_raises_without_python_analysis( + test_case: InternalFailureTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: record_dir: Path = tmp_path / "records" - request: NativeModelAnalysisRequest = analysis_request( - inputs=compile_inputs(project_dir=tmp_path / "project", files=test_case.files), - monkeypatch=monkeypatch, - ) - profile: ExpressionInferenceProfile = request.inference_profile monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + inputs: CompileProjectInputs = compile_inputs( + project_dir=tmp_path / "project", files=test_case.files + ) + python_analyses: list[object] = record_python_model_analyses(monkeypatch=monkeypatch) + duplicate_analysed_model_names(monkeypatch=monkeypatch) - analyses: object = analyze_native_model_sql( - request=replace( - request, - allow_compact_analysis=test_case.allow_compact_analysis, - inference_profile=replace( - profile, binding_catalog=(None, profile.binding_catalog)[test_case.keeps_catalog] - ), + with pytest.raises(native_module.NativeCompilerError) as raised: + _ = assemble_compiled_project( + inputs=inputs, + inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb"), ) - ) - assert analyses is None - assert deferral_kinds(record_dir) == Counter({f"analysis_session:{test_case.expected_kind}": 1}) + assert (str(raised.value), python_analyses, deferral_kinds(record_dir)) == ( + test_case.expected_message, + [], + Counter(), + ) @pytest.mark.parametrize( @@ -569,7 +531,7 @@ def adapter_rule(arguments: tuple[InferredNullability, ...]) -> InferredNullabil monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) if test_case.raised is None: - assert analyze_native_model_sql(request=request) is not None + assert analyze_native_model_sql(request=request).analyses else: with pytest.raises(test_case.raised, match="adapter rule failed"): _ = analyze_native_model_sql(request=request) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py index f34443dbe1..958b0b3d96 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py @@ -2,9 +2,7 @@ from collections.abc import Callable from dataclasses import dataclass, field -from pathlib import Path -from scripts.cold_compile_performance.models import RandomDagProject from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter @@ -359,18 +357,6 @@ class ReshapedStarLineageIntegrationTestCase: expected_sources: tuple[frozenset[tuple[str, str]], ...] -@dataclass(frozen=True) -class SharedBindingQueryCase: - description: str - orders_summary_sql: str - customers_summary_sql: str - expected_shared_queries: int - expected_codes: tuple[str, ...] = () - later_models: tuple[tuple[str, str], ...] = () - expected_lineage: tuple[tuple[str, str], ...] = () - expected_findings: tuple[tuple[str, str], ...] = () - - @dataclass(frozen=True) class SingularAuditAttachmentIntegrationTestCase: description: str @@ -397,113 +383,3 @@ class FunctionArgumentTypeCase: type PreparedCompile = tuple[ CompileOutcome, tuple[str, ...], dict[str, object | None], tuple[bytes, ...] ] - - -@dataclass(frozen=True) -class DataflowOracleCase: - description: str - project: RandomDagProject - reshaped_steps: tuple[tuple[int, ...], ...] - expected_exit_codes: tuple[int, ...] - compile_args: tuple[str, ...] = () - - -@dataclass(frozen=True) -class DataflowFixtureCase: - description: str - fixture: str - expected_exit_code: int - - -@dataclass(frozen=True) -class DataflowDenseCase: - description: str - model_count: int - expected_exit_codes: tuple[int, ...] - - -@dataclass(frozen=True) -class DataflowBuildCase: - description: str - project: RandomDagProject - expected_compile_exit_code: int - expected_build_exit_code: int - - -@dataclass(frozen=True) -class DataflowScheduleCase: - description: str - workers: int - batch_limit: int - max_delay_seconds: float - expected_cold_exit_code: int = 1 - expected_edit_exit_code: int = 1 - - -@dataclass(frozen=True) -class DataflowCaptureCase: - description: str - write_project: Callable[[Path], object] - schedules: tuple[DataflowScheduleCase, ...] - expected_exit_code: int - expected_minimum_shareable_members: int - expected_minimum_shared_reuse: int - expected_difference: str | None = None - - -@dataclass(frozen=True) -class DataflowFailureCase: - description: str - failing_models: tuple[str, ...] - expected_message: str - - -@dataclass(frozen=True) -class DataflowPoolCase: - description: str - compiles: int - minimum_batches: int - expected_pool_threads: int - - -@dataclass(frozen=True) -class DataflowInterruptCase: - description: str - expected_live_workers: tuple[str, ...] - expected_overlaps: tuple[str, ...] - expected_thread_errors: int - expected_notices: int - - -@dataclass(frozen=True) -class DataflowInterruptAfterFaultCase: - description: str - failing_model: str - interrupted_model: str - expected_wave_replays: int - - -@dataclass(frozen=True) -class DataflowStartFailureCase: - description: str - failing_start: int - interrupt_after_start: bool - expected_error_type: type[BaseException] - expected_error: str - expected_live_workers: tuple[str, ...] - expected_notices: int - - -@dataclass(frozen=True) -class BatchedPreparationCase: - description: str - write_project: Callable[[Path], tuple[str, ...]] - reshaped: tuple[int, ...] - expected_exit_code: int - - -@dataclass(frozen=True) -class PerturbedPreparationCase: - description: str - project: RandomDagProject - expected_payloads_match: bool diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py index 21b584ac37..81bd7146b1 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py @@ -1,38 +1,17 @@ from __future__ import annotations -import itertools import json -import random -import shutil -import signal -import threading -import time -from collections.abc import Callable, Mapping, Sequence -from contextlib import suppress +from collections.abc import Callable from pathlib import Path from types import ModuleType -from typing import Any, NamedTuple, cast +from typing import Any -import orjson -import pytest - -import sqlbuild._native as _native -from scripts.cold_compile_performance._helpers.dense_project import write_dense_compile_project -from scripts.cold_compile_performance._helpers.random_dag_project import write_random_dag_project -from scripts.cold_compile_performance.models import RandomDagProject from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter -from sqlbuild.cli.commands.main.entrypoint.entry import main from sqlbuild.compiler.compile._helpers.analysis import compact -from sqlbuild.compiler.compile._helpers.analysis.compact import native_section -from sqlbuild.compiler.compile._helpers.assembly import binding_waves -from sqlbuild.compiler.compile._helpers.assembly import project as project_assembly -from sqlbuild.compiler.compile._helpers.assembly.binding_waves import analyze_binding_waves from sqlbuild.compiler.compile._helpers.sharing import binding as binding_sharing -from sqlbuild.compiler.compile.classes.binding_dataflow import BindingDataflow from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( - CompactBatchPreparation, CompileAdapterContext, CompileAuditInput, CompiledLineageColumnFact, @@ -41,39 +20,13 @@ ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs -from sqlbuild.compiler.frontier.constants import STAGE_CAPTURE_DIR_ENV_VAR -from sqlbuild.compiler.frontier.types import CompilerStage from sqlbuild.compiler.manifest.main.build import build_manifest from sqlbuild.compiler.pipeline.main.compile import run_compile_pipeline from sqlbuild.compiler.pipeline.main.project import compile_project from sqlbuild.compiler.pipeline.models import CompilePipelineOptions, CompilePipelineResult from sqlbuild.compiler.sql_analysis.main import _normalize_analysis_batch as normalize_batch -from sqlbuild.compiler.sql_analysis.types import NativePositionsModule, NativeProjectCatalog from sqlbuild.runtime.contracts.models import ConnectionHooks from sqlbuild.sql_values.types import CollectionRendering -from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( - CompileOutcome, - DataflowScheduleCase, - PreparedCompile, - SharedBindingQueryCase, -) - - -class CompiledProjectRun(NamedTuple): - """One cold compile's exit code, compiled-project capture and shared-analysis reuse.""" - - exit_code: int - capture: Path - shareable_members: int - deduplicated_queries: int - shared_memo_hits: int - - @property - def shared_reuse(self) -> int: - """Return how many member analyses were served by another member's analysis.""" - - return self.deduplicated_queries + self.shared_memo_hits - _REPOSITORY_ROOT: Path = Path(__file__).resolve().parents[6] _NORMALIZATION_MODULES: tuple[ModuleType, ...] = (normalize_batch, binding_sharing, compact) @@ -244,32 +197,6 @@ def lineage_source_pairs(column: CompiledLineageColumnFact) -> frozenset[tuple[s ) -def write_shared_binding_project(*, project_dir: Path, test_case: SharedBindingQueryCase) -> None: - (project_dir / "sqlbuild_project.toml").write_text( - 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n' - ) - models: Path = project_dir / "models" - models.mkdir() - (models / "orders.sql").write_text(_SHARED_BINDING_UPSTREAM_SQL) - (models / "customers.sql").write_text(_SHARED_BINDING_UPSTREAM_SQL) - (models / "orders_summary.sql").write_text(test_case.orders_summary_sql) - (models / "customers_summary.sql").write_text(test_case.customers_summary_sql) - for name, sql in test_case.later_models: - (models / f"{name}.sql").write_text(sql) - - -def trace_native_compact_batches(monkeypatch: pytest.MonkeyPatch) -> list[CompactBatchPreparation]: - original: Callable[..., object] = compact._run_compact_analysis_batch - preparations: list[CompactBatchPreparation] = [] - - def traced(*, preparation: CompactBatchPreparation) -> object: - preparations.append(preparation) - return original(preparation=preparation) - - monkeypatch.setattr(compact, "_run_compact_analysis_batch", traced) - return preparations - - def apply_warehouse_setup_sql(*, project_dir: Path, statements: tuple[str, ...]) -> None: """Create pre-existing warehouse objects in the project's file-backed DuckDB database.""" @@ -282,470 +209,3 @@ def apply_warehouse_setup_sql(*, project_dir: Path, statements: tuple[str, ...]) connection.execute(statement) finally: connection.close() - - -def reshape_random_dag_model(*, project_dir: Path, name: str) -> None: - """Add one leading output column, changing the model's published shape signature.""" - - path: Path = project_dir / "models" / f"{name}.sql" - header, body = path.read_text(encoding="utf-8").split("\n", 1) - path.write_text( - f"{header}\nSELECT CAST(7 AS INTEGER) AS reshaped, *\n" - f"FROM ({body.strip()}) AS reshaped_input\n", - encoding="utf-8", - ) - - -def compile_outcome( - *, project_dir: Path, args: tuple[str, ...], capsys: pytest.CaptureFixture[str] -) -> CompileOutcome: - """Compile through the CLI; return the exit code, timing-free JSON, and artifacts.""" - - return timed_compile_outcome(project_dir=project_dir, args=args, capsys=capsys)[0] - - -def timed_compile_outcome( - *, project_dir: Path, args: tuple[str, ...], capsys: pytest.CaptureFixture[str] -) -> tuple[CompileOutcome, dict[str, object]]: - """Compile through the CLI; return the timing-free outcome and its compile timings.""" - - exit_code: int = main(["--project-dir", str(project_dir), "compile", "--json", *args]) - payload: dict[str, object] = json.loads( - capsys.readouterr().out.replace(str(project_dir), "") - ) - timings: dict[str, object] = cast(dict[str, object], payload.pop("compile_timings", {})) - compiled: Path = project_dir / "target" / "compiled" - return ( - exit_code, - payload, - { - path.relative_to(compiled).as_posix(): path.read_bytes() - for path in compiled.rglob("*.sql") - }, - ), timings - - -def compiled_project_capture( - *, - project_dir: Path, - capture_dir: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> CompiledProjectRun: - """Cold-compile once with stage captures, counting how often shared analyses were reused.""" - - keys: list[object] = [] - deduplicated: list[int] = [] - memo_hits: list[int] = [] - with monkeypatch.context() as capture_patch: - capture_patch.setenv(STAGE_CAPTURE_DIR_ENV_VAR, str(capture_dir)) - record_shared_analysis_reuse( - monkeypatch=capture_patch, keys=keys, deduplicated=deduplicated, memo_hits=memo_hits - ) - exit_code: int = main( - ["--project-dir", str(project_dir), "compile", "--json", "--no-cache"] - ) - _ = capsys.readouterr() - return CompiledProjectRun( - exit_code=exit_code, - capture=next(capture_dir.glob(f"*-{CompilerStage.COMPILED_PROJECT.value}.json")), - shareable_members=len(keys) - len(set(keys)), - deduplicated_queries=sum(deduplicated), - shared_memo_hits=sum(memo_hits), - ) - - -def record_shared_analysis_reuse( - *, - monkeypatch: pytest.MonkeyPatch, - keys: list[object], - deduplicated: list[int], - memo_hits: list[int], -) -> None: - """Record shared-result keys, each batch's deduplicated queries and its memo hits.""" - - member_keys: Callable[..., tuple[object, ...]] = compact.shared_result_keys - - def counted_keys(**kwargs: Any) -> tuple[object, ...]: - computed: tuple[object, ...] = member_keys(**kwargs) - keys.extend(filter(None, computed)) - return computed - - prepare: Callable[..., CompactBatchPreparation] = compact._prepare_compact_analysis_batch - reuse: Callable[..., Mapping[int, object]] = compact.reused_shared_results - - def counted_prepare(**kwargs: Any) -> CompactBatchPreparation: - preparation: CompactBatchPreparation = prepare(**kwargs) - deduplicated.append(len(preparation.cleaned_sql) - len(preparation.queries)) - return preparation - - def counted_reuse(**kwargs: Any) -> Mapping[int, object]: - reused: Mapping[int, object] = reuse(**kwargs) - memo_hits.append(len(reused)) - return reused - - monkeypatch.setattr(compact, "_prepare_compact_analysis_batch", counted_prepare) - monkeypatch.setattr(compact, "reused_shared_results", counted_reuse) - monkeypatch.setattr(compact, "shared_result_keys", counted_keys) - - -def reshape_models(*, project_dir: Path, names: tuple[str, ...], indexes: tuple[int, ...]) -> None: - """Reshape the models at `indexes` of the generated topological name order.""" - - for index in indexes: - reshape_random_dag_model(project_dir=project_dir, name=names[index]) - - -def use_wave_analysis(monkeypatch: pytest.MonkeyPatch) -> None: - """Analyze one topological level at a time, the reference schedule.""" - - monkeypatch.setattr(project_assembly, "analyze_binding_dataflow", analyze_binding_waves) - - -def analyze_single_model_batches(monkeypatch: pytest.MonkeyPatch) -> None: - """Analyze every model in its own native batch on one worker.""" - - monkeypatch.setattr(binding_waves, "_DATAFLOW_WORKERS", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_MIN", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_LIMIT", 1) - - -def perturb_dataflow_schedule( - *, monkeypatch: pytest.MonkeyPatch, case: DataflowScheduleCase, seed: int -) -> None: - """Change worker count and batch size, and delay native completions at random.""" - - rng: random.Random = random.Random(seed) - original: Callable[..., object] = compact._run_compact_analysis_batch - - def delayed(*, preparation: CompactBatchPreparation) -> object: - result: object = original(preparation=preparation) - with native_section(): - time.sleep(rng.random() * case.max_delay_seconds) - return result - - monkeypatch.setattr(binding_waves, "_DATAFLOW_WORKERS", case.workers) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_MIN", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_LIMIT", case.batch_limit) - monkeypatch.setattr(compact, "_run_compact_analysis_batch", delayed) - - -def fail_model_analysis(*, monkeypatch: pytest.MonkeyPatch, model_names: tuple[str, ...]) -> None: - """Raise from analysis of a batch holding a named model, as an internal fault would.""" - - original: Callable[..., tuple[Any, ...]] = project_assembly._analyze_model_sql_requests - failing_names: frozenset[str] = frozenset(model_names) - - def failing(**kwargs: Any) -> tuple[Any, ...]: - batch_names: set[str] = { - request.model_input.model_file.file_path.stem for request in kwargs["requests"] - } - for name in sorted(batch_names & failing_names)[:1]: - _raise_analysis_fault(name) - return original(**kwargs) - - monkeypatch.setattr(project_assembly, "_analyze_model_sql_requests", failing) - - -def _raise_analysis_fault(name: str) -> None: - raise RuntimeError(f"analysis fault in {name}") - - -def analysis_pool_thread_ids(monkeypatch: pytest.MonkeyPatch) -> list[set[str]]: - """Record native analysis threads now, then again after each compact batch.""" - - original: Callable[..., object] = compact._run_compact_analysis_batch - observed: list[set[str]] = [_analysis_thread_ids()] - - def traced(*, preparation: CompactBatchPreparation) -> object: - result: object = original(preparation=preparation) - observed.append(_analysis_thread_ids()) - return result - - monkeypatch.setattr(compact, "_run_compact_analysis_batch", traced) - return observed - - -def _analysis_thread_ids() -> set[str]: - threads_by_name: dict[str, set[str]] = {} - for task in Path("/proc/self/task").iterdir(): - with suppress(OSError): - name: str = (task / "comm").read_text(encoding="utf-8").strip() - threads_by_name.setdefault(name, set()).add(task.name) - return threads_by_name.get(_ANALYSIS_THREAD_NAME, set()) - - -def interrupt_reacquiring_analysis(*, monkeypatch: pytest.MonkeyPatch) -> list[str]: - """Send SIGINT while a worker thread holds the analysis lock and others reacquire it. - - Return overlaps, which name any batch that entered Python analysis while another held it. - """ - - original: Callable[..., object] = compact._run_compact_analysis_batch - holder: threading.Event = threading.Event() - owner: list[str] = [] - overlaps: list[str] = [] - worker_calls: itertools.count[int] = itertools.count() - hold_calls: dict[bool, Callable[[], int]] = { - False: lambda: next(worker_calls), - True: lambda: -1, - } - - def guarded(*, preparation: CompactBatchPreparation) -> object: - overlaps.extend(owner) - owner.append(threading.current_thread().name) - call: int = hold_calls[threading.current_thread() is threading.main_thread()]() - {0: _hold_analysis_lock}.get(call, _skip_hold)(holder) - owner.clear() - result: object = original(preparation=preparation) - with native_section(): - time.sleep(_REACQUIRE_DELAY_SECONDS) - return result - - def interrupt() -> None: - _ = holder.wait(_INTERRUPT_TIMEOUT_SECONDS) - time.sleep(_INTERRUPT_DELAY_SECONDS) - signal.pthread_kill(threading.main_thread().ident or 0, signal.SIGINT) - - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_MIN", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_LIMIT", 1) - monkeypatch.setattr(compact, "_run_compact_analysis_batch", guarded) - threading.Thread(target=interrupt, daemon=True).start() - return overlaps - - -def _hold_analysis_lock(holder: threading.Event) -> None: - holder.set() - time.sleep(_HOLD_SECONDS) - - -def _skip_hold(holder: threading.Event) -> None: - del holder - - -def interrupt_after_analysis_fault( - *, monkeypatch: pytest.MonkeyPatch, failing_model: str, interrupted_model: str -) -> list[int]: - """Fail one batch, then interrupt a batch that started beside it; return wave replays.""" - - original: Callable[..., tuple[Any, ...]] = project_assembly._analyze_model_sql_requests - actions: dict[str, Callable[[str], None]] = { - failing_model: _fail_after_native_delay, - interrupted_model: _interrupt_after_native_delay, - } - replays: list[int] = [] - analyze_waves: Callable[..., Any] = BindingDataflow.analyze_waves - - def failing(**kwargs: Any) -> tuple[Any, ...]: - batch_names: set[str] = { - request.model_input.model_file.file_path.stem for request in kwargs["requests"] - } - for name in sorted(batch_names & actions.keys()): - actions[name](name) - return original(**kwargs) - - def replay(self: BindingDataflow) -> Any: - replays.append(1) - return analyze_waves(self) - - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_MIN", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_LIMIT", 1) - monkeypatch.setattr(project_assembly, "_analyze_model_sql_requests", failing) - monkeypatch.setattr(BindingDataflow, "analyze_waves", replay) - return replays - - -def _fail_after_native_delay(name: str) -> None: - with native_section(): - time.sleep(_REACQUIRE_DELAY_SECONDS) - raise RuntimeError(f"analysis fault in {name}") - - -def _interrupt_after_native_delay(name: str) -> None: - with native_section(): - time.sleep(_HOLD_SECONDS) - raise KeyboardInterrupt(name) - - -def fail_worker_start( - *, monkeypatch: pytest.MonkeyPatch, failing_start: int, interrupt_after_start: bool -) -> list[str]: - """Fail one analysis worker start; return the names of analysis batches' threads. - - The failing start either never starts its thread or starts it and is then interrupted. - """ - - original_start: Callable[[threading.Thread], None] = threading.Thread.start - starts: itertools.count[int] = itertools.count() - batches: list[str] = trace_analysis_threads(monkeypatch) - - def start_then_interrupt(thread: threading.Thread) -> None: - original_start(thread) - raise KeyboardInterrupt(thread.name) - - failing: Callable[[threading.Thread], None] = { - False: _refuse_start, - True: start_then_interrupt, - }[interrupt_after_start] - - def counted_start(thread: threading.Thread) -> None: - {failing_start: failing}.get(next(starts), original_start)(thread) - - def start(thread: threading.Thread) -> None: - {True: counted_start, False: original_start}[ - thread.name.startswith(_MODEL_ANALYSIS_THREAD_PREFIX) - ](thread) - - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_MIN", 1) - monkeypatch.setattr(binding_waves, "_DATAFLOW_BATCH_LIMIT", 1) - monkeypatch.setattr(threading.Thread, "start", start) - return batches - - -def _refuse_start(thread: threading.Thread) -> None: - raise RuntimeError(f"cannot start {thread.name}") - - -def trace_analysis_threads(monkeypatch: pytest.MonkeyPatch) -> list[str]: - """Record the thread that runs each compact analysis batch, slowed to overlap batches.""" - - original: Callable[..., object] = compact._run_compact_analysis_batch - threads: list[str] = [] - - def traced(*, preparation: CompactBatchPreparation) -> object: - threads.append(threading.current_thread().name) - with native_section(): - time.sleep(_REACQUIRE_DELAY_SECONDS) - return original(preparation=preparation) - - monkeypatch.setattr(compact, "_run_compact_analysis_batch", traced) - return threads - - -def live_model_analysis_threads() -> list[str]: - """Return model analysis worker threads that are still alive.""" - - names_by_kind: dict[bool, list[str]] = {} - for thread in threading.enumerate(): - names_by_kind.setdefault(thread.name.startswith(_MODEL_ANALYSIS_THREAD_PREFIX), []).append( - thread.name - ) - return sorted(names_by_kind.get(True, [])) - - -def random_dag_writer(project: RandomDagProject) -> Callable[[Path], tuple[str, ...]]: - return lambda project_dir: write_random_dag_project(project_dir=project_dir, project=project) - - -def fixture_writer(relative: str) -> Callable[[Path], tuple[str, ...]]: - def write(project_dir: Path) -> tuple[str, ...]: - _ = shutil.copytree( - _REPOSITORY_ROOT / relative, - project_dir, - ignore=shutil.ignore_patterns("target", "*.duckdb"), - ) - return () - - return write - - -def dense_writer(model_count: int) -> Callable[[Path], tuple[str, ...]]: - def write(project_dir: Path) -> tuple[str, ...]: - write_dense_compile_project(project_dir=project_dir, model_count=model_count) - return () - - return write - - -def use_per_model_normalization(monkeypatch: pytest.MonkeyPatch) -> None: - """Normalize each batch member with its own native call, the reference preparation.""" - - for module in _NORMALIZATION_MODULES: - monkeypatch.setattr(module, "normalize_analysis_sql_results", _per_model_normalization) - - -def keep_batched_normalization(monkeypatch: pytest.MonkeyPatch) -> None: - """Leave batched normalization in place, the preparation under test.""" - - _ = monkeypatch - - -def perturb_batched_normalization(monkeypatch: pytest.MonkeyPatch) -> None: - """Change the last member of every batched normalization, which comparisons must catch.""" - - batched: Callable[..., list[str | Exception]] = normalize_batch.normalize_analysis_sql_results - - def perturbed(**arguments: Any) -> list[str | Exception]: - results: list[str | Exception] = batched(**arguments) - return [*results[:-1], *(f"{result} " for result in results[-1:])] - - for module in _NORMALIZATION_MODULES: - monkeypatch.setattr(module, "normalize_analysis_sql_results", perturbed) - - -def prepared_compile( - *, - project_dir: Path, - args: tuple[str, ...], - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, - normalization: Callable[[pytest.MonkeyPatch], None], -) -> PreparedCompile: - """Compile once; return the outcome, cache keys, cache metrics, and native batch payloads.""" - - with monkeypatch.context() as patch: - normalization(patch) - preparations: list[CompactBatchPreparation] = trace_native_compact_batches(patch) - cache_keys: list[str] = _trace_analysis_cache_keys(patch) - outcome: CompileOutcome - timings: dict[str, object] - outcome, timings = timed_compile_outcome(project_dir=project_dir, args=args, capsys=capsys) - return ( - outcome, - tuple(sorted(cache_keys)), - {name: timings.get(name) for name in _ANALYSIS_CACHE_METRICS}, - tuple(_preparation_payload(preparation) for preparation in preparations), - ) - - -def _trace_analysis_cache_keys(monkeypatch: pytest.MonkeyPatch) -> list[str]: - original: Callable[..., str] = project_assembly.model_analysis_cache_key - keys: list[str] = [] - - def traced(**arguments: Any) -> str: - key: str = original(**arguments) - keys.append(key) - return key - - monkeypatch.setattr(project_assembly, "model_analysis_cache_key", traced) - return keys - - -def _per_model_normalization( - *, - dialect: str | None, - requests: Sequence[tuple[str, Mapping[str, str] | None, Mapping[str, str] | None]], - catalog: NativeProjectCatalog | None, -) -> list[str | Exception]: - _ = catalog - native: NativePositionsModule = cast(NativePositionsModule, _native) - return [ - native.normalize_analysis_sqls( - dialect=dialect or "generic", - requests=[(sql, dict(stubs or {}), dict(placeholders or {}))], - )[0] - for sql, stubs, placeholders in requests - ] - - -def _preparation_payload(preparation: CompactBatchPreparation) -> bytes: - return orjson.dumps( - { - "cleaned_sql": preparation.cleaned_sql, - "queries": preparation.queries, - "templates": preparation.templates, - "projections": preparation.projections, - "shared_query_indexes": sorted(preparation.shared_query_indexes), - }, - option=orjson.OPT_SORT_KEYS, - ) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py deleted file mode 100644 index e5d0bc806a..0000000000 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_batched_analysis_preparation.py +++ /dev/null @@ -1,201 +0,0 @@ -"""Batched analysis preparation must match per-model preparation byte for byte.""" - -from pathlib import Path - -import pytest - -from scripts.cold_compile_performance.models import RandomDagProject -from sqlbuild.compiler.compile.main import _build_compile_inputs -from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( - BatchedPreparationCase, - PerturbedPreparationCase, - PreparedCompile, -) -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - dense_writer, - fixture_writer, - keep_batched_normalization, - perturb_batched_normalization, - prepared_compile, - random_dag_writer, - reshape_models, - use_per_model_normalization, - use_wave_analysis, -) - -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") - - -@pytest.mark.parametrize( - "test_case", - [ - BatchedPreparationCase( - "inferred shapes", - random_dag_writer(RandomDagProject(seed=11, model_count=48)), - (16,), - 0, - ), - BatchedPreparationCase( - "binding errors mid-graph", - random_dag_writer(RandomDagProject(seed=23, model_count=48, errors=True)), - (24,), - 1, - ), - BatchedPreparationCase( - "run ids and analysis opt-outs", - random_dag_writer( - RandomDagProject(seed=37, model_count=40, run_ids=True, analysis_opt_outs=True) - ), - (20,), - 0, - ), - BatchedPreparationCase( - "reference cycle", - random_dag_writer(RandomDagProject(seed=67, model_count=24, cycle=True)), - (8,), - 0, - ), - BatchedPreparationCase( - "waffle shop fixture", fixture_writer("tests/e2e/fixtures/waffle_shop"), (), 0 - ), - BatchedPreparationCase( - "tidy shop example", fixture_writer("website/examples/tidy-shop"), (), 0 - ), - BatchedPreparationCase("dense single batch", dense_writer(120), (), 0), - ], - ids=lambda case: case.description, -) -def test_given_project_when_compiling_in_waves_then_batched_preparation_matches_per_model( - test_case: BatchedPreparationCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - batched_dir: Path = tmp_path / "batched" - reference_dir: Path = tmp_path / "reference" - names: tuple[str, ...] = test_case.write_project(batched_dir) - _ = test_case.write_project(reference_dir) - monkeypatch.setattr( - _build_compile_inputs, - "resolve_run_id", - lambda *, selected_run_id: selected_run_id or "run-0", - ) - use_wave_analysis(monkeypatch) - for step, reshaped, args in ( - ("cold", (), ()), - ("warm", (), ()), - ("edit", test_case.reshaped, ()), - ("uncached", (), ("--no-cache",)), - ): - reshape_models(project_dir=batched_dir, names=names, indexes=reshaped) - reshape_models(project_dir=reference_dir, names=names, indexes=reshaped) - expected: PreparedCompile = prepared_compile( - project_dir=reference_dir, - args=args, - capsys=capsys, - monkeypatch=monkeypatch, - normalization=use_per_model_normalization, - ) - actual: PreparedCompile = prepared_compile( - project_dir=batched_dir, - args=args, - capsys=capsys, - monkeypatch=monkeypatch, - normalization=keep_batched_normalization, - ) - - assert actual[3] == expected[3], f"{step} native batch payloads differ" - assert actual[1] == expected[1], f"{step} analysis cache keys differ" - assert actual[2] == expected[2], f"{step} analysis cache hits differ" - assert actual[0] == expected[0], f"{step} compile differs from per-model preparation" - assert actual[0][0] == test_case.expected_exit_code - - -@pytest.mark.parametrize( - "test_case", - [ - BatchedPreparationCase( - "inferred shapes", random_dag_writer(RandomDagProject(seed=13, model_count=48)), (), 0 - ), - BatchedPreparationCase( - "binding errors mid-graph", - random_dag_writer(RandomDagProject(seed=29, model_count=48, errors=True)), - (), - 1, - ), - ], - ids=lambda case: case.description, -) -def test_given_duckdb_project_when_compiling_dataflow_with_and_without_cache_then_matches_per_model( - test_case: BatchedPreparationCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - batched_dir: Path = tmp_path / "batched" - reference_dir: Path = tmp_path / "reference" - _ = test_case.write_project(batched_dir) - _ = test_case.write_project(reference_dir) - monkeypatch.setattr( - _build_compile_inputs, - "resolve_run_id", - lambda *, selected_run_id: selected_run_id or "run-0", - ) - for args in ((), (), ("--no-cache",)): - expected: PreparedCompile = prepared_compile( - project_dir=reference_dir, - args=args, - capsys=capsys, - monkeypatch=monkeypatch, - normalization=use_per_model_normalization, - ) - actual: PreparedCompile = prepared_compile( - project_dir=batched_dir, - args=args, - capsys=capsys, - monkeypatch=monkeypatch, - normalization=keep_batched_normalization, - ) - - assert actual[:3] == expected[:3] - assert actual[0][0] == test_case.expected_exit_code - - -@pytest.mark.parametrize( - "test_case", - [PerturbedPreparationCase("last member", RandomDagProject(seed=11, model_count=24), False)], - ids=lambda case: case.description, -) -def test_given_perturbed_batch_result_when_compiling_then_payloads_differ_from_per_model( - test_case: PerturbedPreparationCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - batched_dir: Path = tmp_path / "batched" - reference_dir: Path = tmp_path / "reference" - _ = random_dag_writer(test_case.project)(batched_dir) - _ = random_dag_writer(test_case.project)(reference_dir) - use_wave_analysis(monkeypatch) - - expected: PreparedCompile = prepared_compile( - project_dir=reference_dir, - args=("--no-cache",), - capsys=capsys, - monkeypatch=monkeypatch, - normalization=use_per_model_normalization, - ) - actual: PreparedCompile = prepared_compile( - project_dir=batched_dir, - args=("--no-cache",), - capsys=capsys, - monkeypatch=monkeypatch, - normalization=perturb_batched_normalization, - ) - - assert len(actual[3]) == len(expected[3]) > 0 - assert (actual[3] == expected[3]) is test_case.expected_payloads_match - - -if __name__ == "__main__": - raise SystemExit(pytest.main([__file__, "-n", "auto", "--dist", "loadfile"])) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_oracle.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_oracle.py deleted file mode 100644 index 7e278c7872..0000000000 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_oracle.py +++ /dev/null @@ -1,215 +0,0 @@ -"""Dependency-driven model analysis must compile exactly what wave-by-wave analysis compiles.""" - -import shutil -from pathlib import Path - -import pytest - -from scripts.cold_compile_performance._helpers.dense_project import write_dense_compile_project -from scripts.cold_compile_performance._helpers.random_dag_project import write_random_dag_project -from scripts.cold_compile_performance.models import RandomDagProject -from sqlbuild.cli.commands.main.entrypoint.entry import main -from sqlbuild.compiler.compile.main import _build_compile_inputs -from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( - CompileOutcome, - DataflowBuildCase, - DataflowDenseCase, - DataflowFixtureCase, - DataflowOracleCase, -) -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - compile_outcome, - reshape_models, - use_wave_analysis, -) - -_REPOSITORY_ROOT: Path = Path(__file__).resolve().parents[6] - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowOracleCase( - description="inferred shapes", - project=RandomDagProject(seed=11, model_count=48), - reshaped_steps=((), (), (16,), (24, 40), ()), - expected_exit_codes=(0, 0, 0, 0, 0), - ), - DataflowOracleCase( - description="binding errors mid-graph", - project=RandomDagProject(seed=23, model_count=48, errors=True), - reshaped_steps=((), (), (16,), (24, 40), ()), - expected_exit_codes=(1, 1, 1, 1, 1), - ), - DataflowOracleCase( - description="run ids and analysis opt-outs", - project=RandomDagProject(seed=37, model_count=40, run_ids=True, analysis_opt_outs=True), - reshaped_steps=((), (), (13,), (20,), ()), - expected_exit_codes=(0, 0, 0, 0, 0), - ), - DataflowOracleCase( - description="built-in rules", - project=RandomDagProject(seed=41, model_count=32, rules=("SQBR",)), - reshaped_steps=((), (10,), ()), - expected_exit_codes=(1, 1, 1), - ), - DataflowOracleCase( - description="analysis cache disabled", - project=RandomDagProject(seed=53, model_count=48, errors=True), - reshaped_steps=((), (16,)), - expected_exit_codes=(1, 1), - compile_args=("--no-cache",), - ), - DataflowOracleCase( - description="missing reference", - project=RandomDagProject(seed=61, model_count=24, missing_reference=True), - reshaped_steps=((), (8,)), - expected_exit_codes=(1, 1), - ), - DataflowOracleCase( - description="reference cycle", - project=RandomDagProject(seed=67, model_count=24, cycle=True), - reshaped_steps=((), (8,)), - expected_exit_codes=(0, 0), - ), - ], - ids=lambda case: case.description, -) -def test_given_random_dag_edit_sequence_when_compiling_then_dataflow_matches_waves( - test_case: DataflowOracleCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - waves_dir: Path = tmp_path / "waves" - dataflow_dir: Path = tmp_path / "dataflow" - names: tuple[str, ...] = write_random_dag_project( - project_dir=waves_dir, project=test_case.project - ) - _ = write_random_dag_project(project_dir=dataflow_dir, project=test_case.project) - exit_codes: list[int] = [] - for step, indexes in enumerate(test_case.reshaped_steps): - monkeypatch.setattr( - _build_compile_inputs, - "resolve_run_id", - lambda *, selected_run_id, run_id=f"run-{step}": selected_run_id or run_id, - ) - reshape_models(project_dir=waves_dir, names=names, indexes=indexes) - reshape_models(project_dir=dataflow_dir, names=names, indexes=indexes) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - expected: CompileOutcome = compile_outcome( - project_dir=waves_dir, args=test_case.compile_args, capsys=capsys - ) - actual: CompileOutcome = compile_outcome( - project_dir=dataflow_dir, args=test_case.compile_args, capsys=capsys - ) - uncached: CompileOutcome = compile_outcome( - project_dir=dataflow_dir, args=("--no-cache",), capsys=capsys - ) - exit_codes.append(actual[0]) - - assert actual == expected, f"step {step} differs from wave analysis" - assert actual == uncached, f"step {step} differs from an uncached compile" - - assert tuple(exit_codes) == test_case.expected_exit_codes - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowFixtureCase("waffle shop fixture", "tests/e2e/fixtures/waffle_shop", 0), - DataflowFixtureCase("waffle shop example", "website/examples/waffle-shop", 0), - DataflowFixtureCase("tidy shop example", "website/examples/tidy-shop", 0), - DataflowFixtureCase("hero shop example", "website/examples/hero-shop", 0), - ], - ids=lambda case: case.description, -) -def test_given_repository_fixture_when_compiling_cold_then_dataflow_matches_waves( - test_case: DataflowFixtureCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "project" - _ = shutil.copytree( - _REPOSITORY_ROOT / test_case.fixture, - project_dir, - ignore=shutil.ignore_patterns("target", "*.duckdb"), - ) - monkeypatch.setattr( - _build_compile_inputs, - "resolve_run_id", - lambda *, selected_run_id: selected_run_id or "run-0", - ) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - expected: CompileOutcome = compile_outcome(project_dir=project_dir, args=(), capsys=capsys) - shutil.rmtree(project_dir / "target") - actual: CompileOutcome = compile_outcome(project_dir=project_dir, args=(), capsys=capsys) - - assert actual == expected - assert actual[0] == test_case.expected_exit_code - - -@pytest.mark.parametrize( - "test_case", - [DataflowDenseCase("dense joins unions macros and functions", 64, (0, 0))], - ids=lambda case: case.description, -) -def test_given_dense_fixture_when_compiling_cold_and_warm_then_dataflow_matches_waves( - test_case: DataflowDenseCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - waves_dir: Path = tmp_path / "waves" - dataflow_dir: Path = tmp_path / "dataflow" - write_dense_compile_project(project_dir=waves_dir, model_count=test_case.model_count) - write_dense_compile_project(project_dir=dataflow_dir, model_count=test_case.model_count) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - expected: tuple[CompileOutcome, CompileOutcome] = ( - compile_outcome(project_dir=waves_dir, args=(), capsys=capsys), - compile_outcome(project_dir=waves_dir, args=(), capsys=capsys), - ) - actual: tuple[CompileOutcome, CompileOutcome] = ( - compile_outcome(project_dir=dataflow_dir, args=(), capsys=capsys), - compile_outcome(project_dir=dataflow_dir, args=(), capsys=capsys), - ) - - assert actual == expected - assert (actual[0][0], actual[1][0]) == test_case.expected_exit_codes - - -@pytest.mark.parametrize( - "test_case", - [DataflowBuildCase("inferred shapes", RandomDagProject(seed=79, model_count=24), 0, 0)], - ids=lambda case: case.description, -) -def test_given_random_dag_when_building_on_duckdb_then_every_model_materializes( - test_case: DataflowBuildCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] -) -> None: - project_dir: Path = tmp_path / "orders" - names: tuple[str, ...] = write_random_dag_project( - project_dir=project_dir, project=test_case.project - ) - config: Path = project_dir / "sqlbuild_project.toml" - _ = config.write_text( - config.read_text(encoding="utf-8") - + f'[connection]\ndatabase = "{project_dir / "orders.duckdb"}"\n', - encoding="utf-8", - ) - - compiled: int = main(["--project-dir", str(project_dir), "compile", "--json"]) - compile_output: str = capsys.readouterr().out - built: int = main(["--project-dir", str(project_dir), "build", "--no-tests", "--no-audits"]) - build_output: str = "".join(capsys.readouterr()) - - assert compiled == test_case.expected_compile_exit_code, compile_output - assert built == test_case.expected_build_exit_code, build_output - assert all(name in build_output for name in names) - - -if __name__ == "__main__": - raise SystemExit(pytest.main([__file__, "-n", "auto", "--dist", "loadfile"])) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py deleted file mode 100644 index a3ed09e470..0000000000 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_dataflow_analysis_schedule.py +++ /dev/null @@ -1,330 +0,0 @@ -"""Dataflow analysis output must not depend on worker count, batching, or completion order.""" - -import threading -import time -from pathlib import Path - -import pytest -from _pytest.capture import CaptureResult - -from scripts.cold_compile_performance._helpers.random_dag_project import write_random_dag_project -from scripts.cold_compile_performance.models import RandomDagProject -from scripts.compiler_differential._helpers.comparing.capture import first_capture_difference -from scripts.compiler_differential.main.generate_project import generate_project -from scripts.compiler_differential.models import Divergence -from sqlbuild.cli.commands.main.entrypoint.entry import main -from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( - CompileOutcome, - DataflowCaptureCase, - DataflowFailureCase, - DataflowInterruptAfterFaultCase, - DataflowInterruptCase, - DataflowPoolCase, - DataflowScheduleCase, - DataflowStartFailureCase, -) -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - CompiledProjectRun, - analysis_pool_thread_ids, - compile_outcome, - compiled_project_capture, - fail_model_analysis, - fail_worker_start, - interrupt_after_analysis_fault, - interrupt_reacquiring_analysis, - live_model_analysis_threads, - perturb_dataflow_schedule, - random_dag_writer, - reshape_models, - use_wave_analysis, -) - -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") - -_PROJECT: RandomDagProject = RandomDagProject(seed=97, model_count=40, errors=True) -_SHARED_ANALYSIS_SEED: int = 34 -_CAPTURE_SCHEDULES: tuple[DataflowScheduleCase, ...] = ( - DataflowScheduleCase("one worker with single models", 1, 1, 0.0), - DataflowScheduleCase("four workers with small batches", 4, 2, 0.004), - DataflowScheduleCase("four workers with wide batches", 4, 64, 0.002), -) -_EDITED: tuple[int, ...] = (10, 25) -_REPEATS: tuple[int, ...] = (0, 1) -_SETTLE_SECONDS: float = 0.3 -_INTERRUPT_NOTICE: str = "Interrupted; finishing in-flight model analysis..." - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowScheduleCase("one worker with whole ready sets", 1, 64, 0.0), - DataflowScheduleCase("two workers with single models", 2, 1, 0.004), - DataflowScheduleCase("four workers with small batches", 4, 3, 0.004), - DataflowScheduleCase("four workers with wide batches", 4, 64, 0.002), - ], - ids=lambda case: case.description, -) -def test_given_perturbed_schedule_when_compiling_repeatedly_then_output_is_identical( - test_case: DataflowScheduleCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - reference_dir: Path = tmp_path / "reference" - names: tuple[str, ...] = write_random_dag_project(project_dir=reference_dir, project=_PROJECT) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - cold: CompileOutcome = compile_outcome(project_dir=reference_dir, args=(), capsys=capsys) - reshape_models(project_dir=reference_dir, names=names, indexes=_EDITED) - edit: CompileOutcome = compile_outcome(project_dir=reference_dir, args=(), capsys=capsys) - for repeat in _REPEATS: - project_dir: Path = tmp_path / f"dataflow-{repeat}" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - with monkeypatch.context() as schedule_patch: - perturb_dataflow_schedule(monkeypatch=schedule_patch, case=test_case, seed=repeat) - actual_cold: CompileOutcome = compile_outcome( - project_dir=project_dir, args=(), capsys=capsys - ) - reshape_models(project_dir=project_dir, names=names, indexes=_EDITED) - actual_edit: CompileOutcome = compile_outcome( - project_dir=project_dir, args=(), capsys=capsys - ) - - assert actual_cold == cold, f"cold compile differs in repeat {repeat}" - assert actual_edit == edit, f"edited compile differs in repeat {repeat}" - assert actual_cold[0] == test_case.expected_cold_exit_code - assert actual_edit[0] == test_case.expected_edit_exit_code - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowCaptureCase( - "random dag", - random_dag_writer(_PROJECT), - _CAPTURE_SCHEDULES, - expected_exit_code=1, - expected_minimum_shareable_members=0, - expected_minimum_shared_reuse=0, - ), - DataflowCaptureCase( - "differential seed with shared analyses", - generate_project(seed=_SHARED_ANALYSIS_SEED).write, - _CAPTURE_SCHEDULES, - expected_exit_code=0, - expected_minimum_shareable_members=1, - expected_minimum_shared_reuse=1, - ), - ], - ids=lambda case: case.description, -) -def test_given_perturbed_schedule_when_capturing_compiled_project_then_capture_is_identical( - test_case: DataflowCaptureCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "project" - _ = test_case.write_project(project_dir) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - reference: CompiledProjectRun = compiled_project_capture( - project_dir=project_dir, - capture_dir=tmp_path / "reference", - capsys=capsys, - monkeypatch=monkeypatch, - ) - for index, schedule in enumerate(test_case.schedules): - with monkeypatch.context() as schedule_patch: - perturb_dataflow_schedule(monkeypatch=schedule_patch, case=schedule, seed=index) - actual: CompiledProjectRun = compiled_project_capture( - project_dir=project_dir, - capture_dir=tmp_path / f"schedule-{index}", - capsys=capsys, - monkeypatch=monkeypatch, - ) - - difference: Divergence | None = first_capture_difference( - left=reference.capture, right=actual.capture - ) - assert getattr(difference, "location", None) == test_case.expected_difference, schedule - assert ( - actual.exit_code, - actual.shareable_members >= test_case.expected_minimum_shareable_members, - actual.shared_reuse - >= test_case.expected_minimum_shared_reuse * (schedule.workers == 1), - ) == (test_case.expected_exit_code, True, True), (schedule, actual) - assert ( - reference.exit_code, - reference.shareable_members >= test_case.expected_minimum_shareable_members, - reference.shared_reuse >= test_case.expected_minimum_shared_reuse, - ) == (test_case.expected_exit_code, True, True), reference - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowFailureCase("one failing model", ("orders_020",), "analysis fault in orders_020"), - DataflowFailureCase( - "an upstream failure wins", ("orders_035", "orders_004"), "analysis fault in orders_004" - ), - ], - ids=lambda case: case.description, -) -def test_given_analysis_fault_when_completion_order_varies_then_first_wave_error_is_raised( - test_case: DataflowFailureCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "orders" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - args: list[str] = ["--project-dir", str(project_dir), "compile", "--json", "--no-cache"] - fail_model_analysis(monkeypatch=monkeypatch, model_names=test_case.failing_models) - with monkeypatch.context() as waves_patch: - use_wave_analysis(waves_patch) - with pytest.raises(RuntimeError) as expected: - _ = main(args) - _ = capsys.readouterr() - perturb_dataflow_schedule( - monkeypatch=monkeypatch, - case=DataflowScheduleCase("four workers with delays", 4, 3, 0.004), - seed=len(test_case.failing_models), - ) - with pytest.raises(RuntimeError) as actual: - _ = main(args) - - assert str(actual.value) == str(expected.value) - assert str(actual.value) == test_case.expected_message - - -@pytest.mark.parametrize( - "test_case", - [DataflowPoolCase("two multi-batch compiles", 2, 3, 4)], - ids=lambda case: case.description, -) -def test_given_multi_batch_compiles_when_analyzing_then_each_compile_reuses_one_native_pool( - test_case: DataflowPoolCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "orders" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - for _ in range(test_case.compiles): - observed: list[set[str]] = analysis_pool_thread_ids(monkeypatch) - _ = compile_outcome(project_dir=project_dir, args=("--no-cache",), capsys=capsys) - - started: set[str] = set().union(*observed[1:]) - observed[0] - - assert len(observed) > test_case.minimum_batches - assert len(started) == test_case.expected_pool_threads - - -@pytest.mark.parametrize( - "test_case", - [DataflowInterruptCase("interrupt while workers reacquire the lock", (), (), 0, 1)], - ids=lambda case: case.description, -) -def test_given_sigint_while_workers_contend_for_lock_when_analyzing_then_interrupt_propagates_once( - test_case: DataflowInterruptCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "orders" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - thread_errors: list[threading.ExceptHookArgs] = [] - monkeypatch.setattr(threading, "excepthook", thread_errors.append) - overlaps: list[str] = interrupt_reacquiring_analysis(monkeypatch=monkeypatch) - - with pytest.raises(KeyboardInterrupt): - _ = main(["--project-dir", str(project_dir), "compile", "--json", "--no-cache"]) - captured: CaptureResult[str] = capsys.readouterr() - - assert tuple(live_model_analysis_threads()) == test_case.expected_live_workers - assert tuple(overlaps) == test_case.expected_overlaps - assert len(thread_errors) == test_case.expected_thread_errors - assert captured.err.count(_INTERRUPT_NOTICE) == test_case.expected_notices - assert _INTERRUPT_NOTICE not in captured.out - - -@pytest.mark.parametrize( - "test_case", - [DataflowInterruptAfterFaultCase("interrupt after a fault", "orders_000", "orders_001", 0)], - ids=lambda case: case.description, -) -def test_given_interrupt_after_analysis_fault_when_analyzing_then_interrupt_wins_without_replay( - test_case: DataflowInterruptAfterFaultCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "orders" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - replays: list[int] = interrupt_after_analysis_fault( - monkeypatch=monkeypatch, - failing_model=test_case.failing_model, - interrupted_model=test_case.interrupted_model, - ) - - with pytest.raises(KeyboardInterrupt, match=test_case.interrupted_model): - _ = main(["--project-dir", str(project_dir), "compile", "--json", "--no-cache"]) - _ = capsys.readouterr() - - assert len(replays) == test_case.expected_wave_replays - - -@pytest.mark.parametrize( - "test_case", - [ - DataflowStartFailureCase( - description="third worker cannot start", - failing_start=2, - interrupt_after_start=False, - expected_error_type=RuntimeError, - expected_error="cannot start", - expected_live_workers=(), - expected_notices=0, - ), - DataflowStartFailureCase( - description="interrupt after the first worker starts", - failing_start=0, - interrupt_after_start=True, - expected_error_type=KeyboardInterrupt, - expected_error="sqlbuild-model-analysis-0", - expected_live_workers=(), - expected_notices=1, - ), - ], - ids=lambda case: case.description, -) -def test_given_worker_start_failure_when_analyzing_then_started_workers_stop_before_raising( - test_case: DataflowStartFailureCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - project_dir: Path = tmp_path / "orders" - _ = write_random_dag_project(project_dir=project_dir, project=_PROJECT) - batches: list[str] = fail_worker_start( - monkeypatch=monkeypatch, - failing_start=test_case.failing_start, - interrupt_after_start=test_case.interrupt_after_start, - ) - - with pytest.raises(test_case.expected_error_type, match=test_case.expected_error): - _ = main(["--project-dir", str(project_dir), "compile", "--json", "--no-cache"]) - live_at_failure: list[str] = live_model_analysis_threads() - batches_at_failure: int = len(batches) - captured: CaptureResult[str] = capsys.readouterr() - time.sleep(_SETTLE_SECONDS) - - assert tuple(live_at_failure) == test_case.expected_live_workers - assert len(batches) == batches_at_failure - assert captured.err.count(_INTERRUPT_NOTICE) == test_case.expected_notices - assert _INTERRUPT_NOTICE not in captured.out - - -if __name__ == "__main__": - raise SystemExit(pytest.main([__file__, "-n", "auto", "--dist", "loadfile"])) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py index 0f9fde469e..52aeb7871c 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py @@ -14,7 +14,7 @@ ExpressionMemoCase, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") +pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_expression_shapes") @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py deleted file mode 100644 index 608743d829..0000000000 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_shared_binding_queries.py +++ /dev/null @@ -1,351 +0,0 @@ -"""Binding-mode models with identical SQL shapes and inputs share one native query.""" - -import json -from pathlib import Path -from typing import Any - -import pytest - -from sqlbuild.cli.commands.main.entrypoint.entry import main -from sqlbuild.compiler.compile.models import CompactBatchPreparation -from tests.integration.src.sqlbuild.compiler.pipeline._test_types import SharedBindingQueryCase -from tests.integration.src.sqlbuild.compiler.pipeline.helpers import ( - analyze_single_model_batches, - trace_native_compact_batches, - write_shared_binding_project, -) - -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") - - -@pytest.mark.parametrize( - "test_case", - [ - SharedBindingQueryCase( - description="identical shapes and inputs share one query", - orders_summary_sql='MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("orders")', - customers_summary_sql='MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("customers")', - expected_shared_queries=1, - ), - SharedBindingQueryCase( - description="a relation name used as a qualifier is not shared", - orders_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT orders.id FROM __ref("orders") AS orders' - ), - customers_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT customers.id FROM __ref("customers") AS customers' - ), - expected_shared_queries=0, - ), - SharedBindingQueryCase( - description="an authored lowercase stub-named CTE is not shared", - orders_summary_sql=( - "MODEL (description 'Test model.', materialized view); WITH __sqlbuild_project_input_0 AS " - '(SELECT CAST(2 AS VARCHAR) AS id) SELECT id FROM __ref("orders")' - ), - customers_summary_sql=( - "MODEL (description 'Test model.', materialized view); WITH __sqlbuild_project_input_0 AS " - '(SELECT CAST(2 AS VARCHAR) AS id) SELECT id FROM __ref("customers")' - ), - expected_shared_queries=0, - later_models=( - ( - "next_orders", - 'MODEL (description "Test model.", materialized view); SELECT id + 1 AS next_id FROM __ref("orders_summary")', - ), - ), - ), - SharedBindingQueryCase( - description="an authored uppercase stub-named CTE is not shared", - orders_summary_sql=( - "MODEL (description 'Test model.', materialized view); WITH __SQLBUILD_PROJECT_INPUT_0 AS " - '(SELECT CAST(2 AS VARCHAR) AS id) SELECT id FROM __ref("orders")' - ), - customers_summary_sql=( - "MODEL (description 'Test model.', materialized view); WITH __SQLBUILD_PROJECT_INPUT_0 AS " - '(SELECT CAST(2 AS VARCHAR) AS id) SELECT id FROM __ref("customers")' - ), - expected_shared_queries=0, - later_models=( - ( - "next_orders", - 'MODEL (description "Test model.", materialized view); SELECT id + 1 AS next_id FROM __ref("orders_summary")', - ), - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_models_with_equal_shapes_when_compiling_then_shares_only_lossless_queries( - test_case: SharedBindingQueryCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - write_shared_binding_project(project_dir=tmp_path, test_case=test_case) - preparations: list[CompactBatchPreparation] = trace_native_compact_batches(monkeypatch) - - assert main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) == 0 - payload: dict[str, Any] = json.loads(capsys.readouterr().out) - - assert payload["diagnostics"] == [] - assert ( - sum(len(preparation.shared_query_indexes) for preparation in preparations) - == test_case.expected_shared_queries - ) - for model, upstream in (("orders_summary", "orders"), ("customers_summary", "customers")): - assert ( - main( - [ - "--project-dir", - str(tmp_path), - "lineage", - f"{model}.id", - "--format", - "json", - "--direction", - "upstream", - ] - ) - == 0 - ) - lineage: dict[str, Any] = json.loads(capsys.readouterr().out) - assert [step["source"]["resource_name"] for step in lineage["trace"]] == [upstream] - - -@pytest.mark.parametrize( - "test_case", - [ - SharedBindingQueryCase( - description="findings after the relation keep each model's names and positions", - orders_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id FROM __ref("orders") WHERE missing > 0' - ), - customers_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id FROM __ref("customers") WHERE missing > 0' - ), - expected_shared_queries=1, - expected_codes=("B002",), - ), - ], - ids=lambda case: case.description, -) -def test_given_shared_query_with_finding_when_compiling_then_reports_each_model_exactly( - test_case: SharedBindingQueryCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - write_shared_binding_project(project_dir=tmp_path, test_case=test_case) - preparations: list[CompactBatchPreparation] = trace_native_compact_batches(monkeypatch) - - assert main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) == 1 - payload: dict[str, Any] = json.loads(capsys.readouterr().out) - - assert ( - sum(len(preparation.shared_query_indexes) for preparation in preparations) - == test_case.expected_shared_queries - ) - findings: dict[str, dict[str, Any]] = { - diagnostic["resource_name"]: diagnostic for diagnostic in payload["diagnostics"] - } - assert sorted(findings) == ["customers_summary", "orders_summary"] - for model, upstream, sql in ( - ("orders_summary", "orders", test_case.orders_summary_sql), - ("customers_summary", "customers", test_case.customers_summary_sql), - ): - finding: dict[str, Any] = findings[model] - assert (finding["code"],) == test_case.expected_codes - assert finding["message"] == f"Unknown column 'missing' in {upstream}" - assert finding["location"]["column"] == sql.index("missing") + 1 - assert finding["location"]["end_column"] == sql.index("missing") + len("missing") + 1 - - -@pytest.mark.parametrize( - "test_case", - [ - SharedBindingQueryCase( - description="later-wave members reuse an exact shared result", - orders_summary_sql='MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("orders")', - customers_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("customers")' - ), - expected_shared_queries=1, - later_models=( - ( - "orders_rollup", - 'MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("orders_summary")', - ), - ( - "customers_rollup", - 'MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("customers_summary")', - ), - ), - expected_lineage=( - ("orders_rollup", "orders_summary"), - ("customers_rollup", "customers_summary"), - ), - ) - ], - ids=lambda case: case.description, -) -def test_given_later_wave_members_with_equal_keys_when_compiling_then_reuses_shared_result( - test_case: SharedBindingQueryCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - write_shared_binding_project(project_dir=tmp_path, test_case=test_case) - preparations: list[CompactBatchPreparation] = trace_native_compact_batches(monkeypatch) - - assert main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) == 0 - payload: dict[str, Any] = json.loads(capsys.readouterr().out) - - assert payload["diagnostics"] == [] - assert ( - sum(len(preparation.shared_query_indexes) for preparation in preparations) - == test_case.expected_shared_queries - ) - analyzed_sql: list[str] = [] - for preparation in preparations: - analyzed_sql.extend(str(query["sql"]) for query in preparation.queries) - assert not any("_summary" in sql for sql in analyzed_sql) - for model, upstream in test_case.expected_lineage: - assert ( - main( - [ - "--project-dir", - str(tmp_path), - "lineage", - f"{model}.amount", - "--format", - "json", - "--direction", - "upstream", - "--depth", - "1", - ] - ) - == 0 - ) - lineage: dict[str, Any] = json.loads(capsys.readouterr().out) - assert [step["source"]["resource_name"] for step in lineage["trace"]] == [upstream] - - -@pytest.mark.parametrize( - "test_case", - [ - SharedBindingQueryCase( - description="members analyzed in separate batches keep their own sources", - orders_summary_sql='MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("orders")', - customers_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("customers")' - ), - expected_shared_queries=0, - expected_lineage=( - ("orders_summary", "orders"), - ("customers_summary", "customers"), - ), - ) - ], - ids=lambda case: case.description, -) -def test_given_equal_keys_in_separate_batches_when_tracing_lineage_then_each_model_keeps_its_source( - test_case: SharedBindingQueryCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - write_shared_binding_project(project_dir=tmp_path, test_case=test_case) - analyze_single_model_batches(monkeypatch) - - for model, upstream in test_case.expected_lineage: - assert ( - main( - [ - "--project-dir", - str(tmp_path), - "lineage", - f"{model}.amount", - "--format", - "json", - "--direction", - "upstream", - "--depth", - "1", - "--mode", - "fast", - ] - ) - == 0 - ) - lineage: dict[str, Any] = json.loads(capsys.readouterr().out) - assert [step["source"]["resource_name"] for step in lineage["trace"]] == [upstream] - - -@pytest.mark.parametrize( - "test_case", - [ - SharedBindingQueryCase( - description="a later-wave member of a group with findings keeps its own names", - orders_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id FROM __ref("orders") WHERE missing > 0' - ), - customers_summary_sql=( - 'MODEL (description "Test model.", materialized view); SELECT id FROM __ref("customers") WHERE missing > 0' - ), - expected_shared_queries=2, - expected_codes=("B002",), - later_models=( - ( - "orders_mid", - 'MODEL (description "Test model.", materialized view); SELECT id, amount FROM __ref("orders")', - ), - ( - "orders_late", - 'MODEL (description "Test model.", materialized view); SELECT id FROM __ref("orders_mid") WHERE missing > 0', - ), - ), - expected_findings=( - ("orders_summary", "orders"), - ("customers_summary", "customers"), - ("orders_late", "orders_mid"), - ), - ) - ], - ids=lambda case: case.description, -) -def test_given_later_wave_member_of_group_with_findings_when_compiling_then_reports_it_exactly( - test_case: SharedBindingQueryCase, - tmp_path: Path, - capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, -) -> None: - write_shared_binding_project(project_dir=tmp_path, test_case=test_case) - preparations: list[CompactBatchPreparation] = trace_native_compact_batches(monkeypatch) - - assert main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) == 1 - payload: dict[str, Any] = json.loads(capsys.readouterr().out) - - assert ( - sum(len(preparation.shared_query_indexes) for preparation in preparations) - == test_case.expected_shared_queries - ) - findings: dict[str, dict[str, Any]] = { - diagnostic["resource_name"]: diagnostic for diagnostic in payload["diagnostics"] - } - assert sorted(findings) == sorted(model for model, _ in test_case.expected_findings) - sql_by_model: dict[str, str] = { - "orders_summary": test_case.orders_summary_sql, - "customers_summary": test_case.customers_summary_sql, - **dict(test_case.later_models), - } - for model, upstream in test_case.expected_findings: - finding: dict[str, Any] = findings[model] - assert (finding["code"],) == test_case.expected_codes - assert finding["message"] == f"Unknown column 'missing' in {upstream}" - assert finding["location"]["column"] == sql_by_model[model].index("missing") + 1 - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py index 39fb797bb2..8a5e4b9279 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py @@ -13,8 +13,6 @@ ) from sqlbuild.compiler.compile.types import AttachedAuditTargetKind, SqlTestMode from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, InferredNullability, ) from sqlbuild.compiler.references.types import SqlReferenceKind @@ -84,12 +82,6 @@ class AuditFactoryAttachmentTestCase: expected_warning_code: str | None = None -@dataclass(frozen=True) -class AnalysisCacheTestCase: - description: str - expected_count: int - - @dataclass(frozen=True) class NativeSqlTestExtractionParityTestCase: description: str @@ -884,14 +876,6 @@ class ExpectedCountTestCase: expected_count: int -@dataclass(frozen=True) -class CachedLineageRoundTripTestCase: - description: str - transform_kind: ColumnTransformKind - confidence: ColumnLineageConfidence - expected_compact: bool - - @dataclass(frozen=True) class ClassifyDirectLogicSqlTestCtesErrorTestCase: description: str @@ -1102,14 +1086,6 @@ class QualifiedReferenceScanTestCase: expected_names: frozenset[str] -@dataclass(frozen=True) -class CompactLineageCacheEncodingTestCase: - description: str - output_column: str - resource_names: dict[int, str] - expected_lineage_columns: int = 2 - - @dataclass(frozen=True) class RequiredDescriptionUnitCase: """One resource kind checked by the required-description compile rule.""" diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_dynamic_pivot.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_dynamic_pivot.py index 812f5c5c4a..b662a814da 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_dynamic_pivot.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_dynamic_pivot.py @@ -2,10 +2,10 @@ import pytest -from sqlbuild.compiler.compile._helpers.analysis.dynamic_pivot import ( - analyze_dynamic_column_contract, +from sqlbuild.compiler.analysis_session._helpers.dynamic_contracts import ( + native_dynamic_contracts, ) -from sqlbuild.compiler.compile.models import DynamicColumnContractProof +from sqlbuild.compiler.analysis_session.models import NativePivotTables from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily from tests.unit.src.sqlbuild.compiler.compile._helpers.analysis._test_types import ( DynamicPivotAnalysisTestCase, @@ -147,41 +147,48 @@ def test_given_dynamic_family_when_analyzing_output_then_requires_supported_closed_shape( test_case: DynamicPivotAnalysisTestCase, ) -> None: - proof: DynamicColumnContractProof | None = analyze_dynamic_column_contract( - query_sql=test_case.query_sql, - dialect=test_case.dialect, - families=( - SchemaDynamicColumnFamily( - name="category_amounts", - pivot_column="category", - value_column="amount", - aggregate=test_case.aggregate, - type="DECIMAL(12,2)", - ), - ), - column_types_by_table={ - "order_amounts": { - "customer_id": "INTEGER", - "category": "VARCHAR", - "amount": "DECIMAL(12,2)", - "country": "VARCHAR", + (proof,) = native_dynamic_contracts( + session=None, + tables=NativePivotTables( + dialect=test_case.dialect, + column_types_by_table={ + "order_amounts": { + "customer_id": "INTEGER", + "category": "VARCHAR", + "amount": "DECIMAL(12,2)", + "country": "VARCHAR", + }, + "partial_order_amounts": { + "customer_id": "INTEGER", + "category": "VARCHAR", + "amount": "DECIMAL(12,2)", + }, }, - "partial_order_amounts": { - "customer_id": "INTEGER", - "category": "VARCHAR", - "amount": "DECIMAL(12,2)", + authoritative_column_types_by_table={ + "order_amounts": { + "customer_id": "INTEGER", + "category": "VARCHAR", + "amount": "DECIMAL(12,2)", + "country": "VARCHAR", + } }, - }, - authoritative_column_types_by_table={ - "order_amounts": { - "customer_id": "INTEGER", - "category": "VARCHAR", - "amount": "DECIMAL(12,2)", - "country": "VARCHAR", - } - }, - column_nullability_by_table={}, - dynamic_families_by_table={}, + column_nullability_by_table={}, + dynamic_families_by_table={}, + ), + models=( + ( + test_case.query_sql, + ( + SchemaDynamicColumnFamily( + name="category_amounts", + pivot_column="category", + value_column="amount", + aggregate=test_case.aggregate, + type="DECIMAL(12,2)", + ), + ), + ), + ), ) assert proof is not None diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py index 98531f3687..06adc0c849 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py @@ -1,4 +1,3 @@ -import sqlite3 from collections.abc import Callable from dataclasses import dataclass, replace from pathlib import Path @@ -26,9 +25,7 @@ from sqlbuild.compiler.compile.main._assemble_project import assemble_project from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( - AnalysisCacheContext, CompileAdapterContext, - CompileAnalysisSelection, CompiledDirectLogicSqlTestPayload, CompiledLineageColumnFact, CompiledLineageSourceFact, @@ -75,7 +72,6 @@ from sqlbuild.compiler.model_loop.main._build_native_declaration_contexts import ( build_native_declaration_contexts, ) -from sqlbuild.compiler.pipeline.main.compiled_project import build_compiled_project from sqlbuild.compiler.planner._helpers.graph.core import build_execution_upstream_deps from sqlbuild.compiler.planner._helpers.resolve.refs import resolve_ref_references from sqlbuild.compiler.scopes.main.build_scope_lookup import build_scope_lookup @@ -252,25 +248,6 @@ def compile_project_inputs(*, project_dir: Path) -> CompileProjectInputs: ) -def compile_project_with_cache( - *, - project_dir: Path, - analysis_selection: CompileAnalysisSelection | None = None, - no_cache: bool = False, -) -> CompiledProject: - """Compile a discovered DuckDB fixture through the cache-enabled project boundary.""" - - effective_selection: CompileAnalysisSelection = replace( - analysis_selection or CompileAnalysisSelection(), - no_cache=no_cache, - ) - return build_compiled_project( - discovered_inputs=discover_project_inputs(project_dir=project_dir), - adapter=DuckDbAdapter(), - analysis_selection=effective_selection, - ) - - def compiled_sql_test_expected_model_names(test: CompiledSqlTest) -> tuple[str, ...]: getters: MappingProxyType[type[object], Callable[[object], tuple[str, ...]]] = MappingProxyType( { @@ -516,17 +493,6 @@ def write_scoped_macro_orders_project(project_dir: Path) -> None: ) -def stored_analysis_contents(*, context: AnalysisCacheContext, cache_key: str) -> str: - """Return the raw stored payload of one analysis cache entry.""" - - database_path: Path = next(context.root.rglob("model-analysis.sqlite3")) - with sqlite3.connect(database_path) as connection: - row: tuple[str] = connection.execute( - "SELECT payload FROM model_analysis WHERE cache_key = ?", (cache_key,) - ).fetchone() - return row[0] - - REQUIRED_DESCRIPTION_BASE_INPUTS: DiscoveredProjectInputs = DiscoveredProjectInputs( project_config=ProjectConfig(name="orders", adapter="duckdb"), local_config=LocalConfig(), diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py deleted file mode 100644 index 6fd230b52e..0000000000 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass - -from sqlbuild.compiler.compile.models import DynamicColumnContractProof, ModelSqlAnalysis -from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily - - -@dataclass(frozen=True) -class DynamicColumnContractDispatchTestCase: - description: str - sql_analysis: ModelSqlAnalysis | None - native_proof: DynamicColumnContractProof | None - dialect: str - families: tuple[SchemaDynamicColumnFamily, ...] - expected_proof: DynamicColumnContractProof | None diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py deleted file mode 100644 index 8addc9e694..0000000000 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py +++ /dev/null @@ -1,114 +0,0 @@ -"""A model's native analysis may carry its dynamic pivot proof; otherwise Python proves it.""" - -from __future__ import annotations - -from functools import partial - -import pytest - -from sqlbuild.compiler.compile._helpers.analysis.dynamic_pivot import ( - analyze_dynamic_column_contract, -) -from sqlbuild.compiler.compile._helpers.native_stages.assembly import ( - dynamic_column_contract_by_engine, -) -from sqlbuild.compiler.compile.models import ( - DynamicColumnContractProof, - ModelSqlAnalysis, - PolyglotAnalysisResult, -) -from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily -from tests.unit.src.sqlbuild.compiler.compile._helpers.native_stages._test_types import ( - DynamicColumnContractDispatchTestCase, -) - -_NATIVE_PROOF: DynamicColumnContractProof = DynamicColumnContractProof( - output_proven=False, failure_reason="native session proof" -) -_PYTHON_UNSUPPORTED_PROOF: DynamicColumnContractProof = DynamicColumnContractProof( - output_proven=False, - failure_reason=("adapter dialect 'bigquery' does not support compiler-proven dynamic pivots"), -) -_FAMILY: SchemaDynamicColumnFamily = SchemaDynamicColumnFamily( - name="category_amounts", - pivot_column="category", - value_column="amount", - aggregate="MAX", - type="DECIMAL(12,2)", -) -_ANALYSIS: PolyglotAnalysisResult = PolyglotAnalysisResult(analysis_succeeded=True) - - -@pytest.mark.parametrize( - "test_case", - ( - DynamicColumnContractDispatchTestCase( - description="native analysis proof replaces the python proof", - native_proof=None, - sql_analysis=ModelSqlAnalysis( - polyglot_analysis=_ANALYSIS, - placeholders=None, - dynamic_column_contract=_NATIVE_PROOF, - ), - dialect="bigquery", - families=(_FAMILY,), - expected_proof=_NATIVE_PROOF, - ), - DynamicColumnContractDispatchTestCase( - description="analysis without a proof falls back to the python proof", - native_proof=None, - sql_analysis=ModelSqlAnalysis(polyglot_analysis=_ANALYSIS, placeholders=None), - dialect="bigquery", - families=(_FAMILY,), - expected_proof=_PYTHON_UNSUPPORTED_PROOF, - ), - DynamicColumnContractDispatchTestCase( - description="unanalyzed model falls back to the python proof", - native_proof=None, - sql_analysis=None, - dialect="bigquery", - families=(_FAMILY,), - expected_proof=_PYTHON_UNSUPPORTED_PROOF, - ), - DynamicColumnContractDispatchTestCase( - description="a batched native proof answers an unanalyzed model", - native_proof=_NATIVE_PROOF, - sql_analysis=None, - dialect="bigquery", - families=(_FAMILY,), - expected_proof=_NATIVE_PROOF, - ), - DynamicColumnContractDispatchTestCase( - description="model without dynamic families has no proof", - native_proof=None, - sql_analysis=ModelSqlAnalysis(polyglot_analysis=_ANALYSIS, placeholders=None), - dialect="snowflake", - families=(), - expected_proof=None, - ), - ), - ids=lambda case: case.description, -) -def test_given_model_analysis_when_proving_dynamic_contract_then_native_proof_takes_precedence( - test_case: DynamicColumnContractDispatchTestCase, -) -> None: - proof: DynamicColumnContractProof | None = dynamic_column_contract_by_engine( - sql_analysis=test_case.sql_analysis, - native_proof=test_case.native_proof, - python_proof=partial( - analyze_dynamic_column_contract, - query_sql="SELECT * FROM order_amounts", - dialect=test_case.dialect, - families=test_case.families, - column_types_by_table={}, - authoritative_column_types_by_table={}, - column_nullability_by_table={}, - dynamic_families_by_table={}, - ), - ) - - assert proof == test_case.expected_proof - - -if __name__ == "__main__": - pytest.main([__file__, "-vv"]) diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py deleted file mode 100644 index 3213343f46..0000000000 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_analysis_cache.py +++ /dev/null @@ -1,1174 +0,0 @@ -from __future__ import annotations - -import json -import sqlite3 -from collections.abc import Callable -from pathlib import Path -from unittest.mock import Mock - -import pytest - -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis import cache as analysis_cache -from sqlbuild.compiler.compile._helpers.analysis.cache import ( - build_analysis_cache_context, - model_analysis_cache_key, - read_model_analyses, - write_model_analyses, -) -from sqlbuild.compiler.compile._helpers.assembly import project as assembly_project -from sqlbuild.compiler.compile._helpers.refs import cache as reference_cache -from sqlbuild.compiler.compile.constants import COMPILE_CACHE_DISABLE_ENV_VAR -from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.compile.models import ( - AnalysisCacheContext, - CompactLineageFacts, - CompileAnalysisSelection, - CompiledLineageColumnFact, - CompiledLineageSourceFact, - CompiledModel, - CompiledProject, - CompileSqlReference, - PolyglotAnalysisResult, -) -from sqlbuild.compiler.frontier.main.compiler_cache_directory import compiler_cache_directory -from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, - InferredNullability, -) -from sqlbuild.compiler.profiling.main.collect import collect_compile_timings -from sqlbuild.compiler.references.types import SqlReferenceKind -from tests.unit.src.sqlbuild.compiler.compile._helpers._test_types import ( - AnalysisCacheTestCase, - CachedLineageRoundTripTestCase, - CompactLineageCacheEncodingTestCase, -) -from tests.unit.src.sqlbuild.compiler.compile._helpers.helpers import ( - compile_project_with_cache, - stored_analysis_contents, -) - -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_analysis") - -_CACHE_REPO_FILES: dict[str, str] = { - "sqlbuild_project.toml": 'name = "cache_demo"\nadapter = "duckdb"\n', - "sources/raw.yml": """ -sources: - - name: raw_orders - description: Test source raw_orders. - contract: enforced - expression: "(SELECT 1 AS order_id)" - columns: - - name: order_id - type: INTEGER - nullable: false -""".strip() - + "\n", - "models/orders.sql": """ -MODEL (description "Test model orders."); - -SELECT order_id FROM __source("raw_orders") -""".strip() - + "\n", -} -_SELECTION_REPO_FILES: dict[str, str] = { - "sqlbuild_project.toml": 'name = "selection_demo"\nadapter = "duckdb"\n', - "models/root.sql": "MODEL (description 'Test model root.');\n\nSELECT 1 AS id\n", - "models/middle.sql": 'MODEL (description "Test model middle.");\n\nSELECT id FROM __ref("root")\n', - "models/leaf.sql": 'MODEL (description "Test model leaf.");\n\nSELECT id FROM __ref("middle")\n', - "models/unrelated.sql": "MODEL (description 'Test model unrelated.');\n\nSELECT 2 AS id\n", -} - - -@pytest.mark.parametrize( - "test_case", - ( - AnalysisCacheTestCase( - description="changed compiler semantics invalidate prior analysis", - expected_count=1, - ), - ), - ids=lambda case: case.description, -) -def test_given_changed_analysis_algorithm_when_compiling_then_refreshes_cached_facts( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - monkeypatch.setattr( - analysis_cache, "_ANALYSIS_ALGORITHM_FINGERPRINT", "synthetic-next-algorithm" - ) - with collect_compile_timings() as changed_metrics: - changed_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - with collect_compile_timings() as normal_metrics: - normal_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert changed_metrics.as_milliseconds()["analysis_cache_misses"] == test_case.expected_count - assert changed_metrics.as_milliseconds()["analysis_entry_cache_hits"] == 0 - assert changed_metrics.as_milliseconds()["analysis_batch_cache_hits"] == 0 - assert normal_metrics.as_milliseconds()["analysis_entry_cache_hits"] == test_case.expected_count - assert normal_project.models == changed_project.models == cold_project.models - assert normal_project.diagnostics == cold_project.diagnostics - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="successful cache hit", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_successful_analysis_when_compiling_again_then_reuses_identical_cached_facts( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock( - side_effect=AssertionError("Polyglot analysis must not run on a cache hit") - ) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - signature_builder: Mock = Mock( - side_effect=AssertionError("output signatures must not be rebuilt on a cache hit") - ) - monkeypatch.setattr(assembly_project, "model_analysis_output_signature", signature_builder) - reference_scanner: Mock = Mock( - side_effect=AssertionError("SQL references must not be scanned on a cache hit") - ) - monkeypatch.setattr(reference_cache, "scan_sql_reference_calls", reference_scanner) - - warm_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert warm_project.models == cold_project.models - assert warm_project.diagnostics == cold_project.diagnostics - analyzer.assert_not_called() - signature_builder.assert_not_called() - reference_scanner.assert_not_called() - assert len(tuple((compiler_cache_directory(tmp_path)).rglob("*.sqlite3"))) == ( - test_case.expected_count + 1 - ) - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="completed compact cache reuse", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_completed_compact_cache_when_compiling_again_then_skips_analysis_and_binding( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - monkeypatch.setattr( - assembly_project, "_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT", test_case.expected_count - ) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock(side_effect=AssertionError("completed facts must not be reanalyzed")) - validator: Mock = Mock(side_effect=AssertionError("completed bindings must not be revalidated")) - monkeypatch.setattr(assembly_project, "_analyze_model_sql_requests", analyzer) - monkeypatch.setattr(assembly_project, "get_schema_validations", validator) - - warm_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert warm_project.models == cold_project.models - assert warm_project.diagnostics == cold_project.diagnostics - analyzer.assert_not_called() - validator.assert_not_called() - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="exact compact batch reuse", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_exact_compact_batch_when_entry_rows_are_absent_then_warm_compile_reuses_batch( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - monkeypatch.setattr(assembly_project, "_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT", 1) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = compiler_cache_directory(tmp_path) / "v13" / "model-analysis.sqlite3" - with sqlite3.connect(cache_path) as connection: - batch_count: int = connection.execute( - "SELECT COUNT(*) FROM model_analysis_compact_batch" - ).fetchone()[0] - _ = connection.execute("DELETE FROM model_analysis") - - with collect_compile_timings() as metrics: - warm_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert batch_count == 1 - assert warm_project.models == cold_project.models - assert warm_project.diagnostics == cold_project.diagnostics - assert metrics.as_milliseconds()["analysis_batch_cache_hits"] == test_case.expected_count - assert metrics.as_milliseconds()["analysis_entry_cache_hits"] == 0 - assert metrics.as_milliseconds()["analysis_cache_misses"] == 0 - assert metrics.as_milliseconds()["analysis_cache_bypasses"] == 0 - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="one changed compact model", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_one_changed_model_when_entry_rows_are_absent_then_compact_batch_reuses_unchanged( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - monkeypatch.setattr(assembly_project, "_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT", 1) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = compiler_cache_directory(tmp_path) / "v13" / "model-analysis.sqlite3" - with sqlite3.connect(cache_path) as connection: - _ = connection.execute("DELETE FROM model_analysis") - (tmp_path / "models" / "unrelated.sql").write_text( - "MODEL (description 'Test model unrelated.');\n\nSELECT 3 AS id\n", - encoding="utf-8", - ) - - changed_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - models_by_name: dict[str, CompiledModel] = { - model.name: model for model in changed_project.models - } - cold_models_by_name: dict[str, CompiledModel] = { - model.name: model for model in cold_project.models - } - assert models_by_name["root"] == cold_models_by_name["root"] - assert models_by_name["middle"] == cold_models_by_name["middle"] - assert models_by_name["leaf"] == cold_models_by_name["leaf"] - assert models_by_name["unrelated"].query_sql == "SELECT 3 AS id" - assert len(models_by_name) == test_case.expected_count - assert changed_project.diagnostics == () - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="corrupt compact batch fallback", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_corrupt_compact_batch_when_entry_rows_are_valid_then_warm_compile_falls_back( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - monkeypatch.setattr(assembly_project, "_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT", 1) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = compiler_cache_directory(tmp_path) / "v13" / "model-analysis.sqlite3" - with sqlite3.connect(cache_path) as connection: - _ = connection.execute( - "UPDATE model_analysis_compact_batch SET payload = ?", - (b'{"version":1,"response":"tampered"}',), - ) - - warm_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert warm_project.models == cold_project.models - assert len(warm_project.models) == test_case.expected_count - assert warm_project.diagnostics == cold_project.diagnostics - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="changed SQL cache miss", expected_count=2),), - ids=lambda case: case.description, -) -def test_given_changed_expanded_sql_when_compiling_then_writes_a_new_analysis_object( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - changed_sql = 'MODEL (description "Test model.");\n\nSELECT order_id + 1 AS order_id FROM __source("raw_orders")\n' - (tmp_path / "models" / "orders.sql").write_text(changed_sql, encoding="utf-8") - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - changed_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - assert changed_project.models[0].query_sql != cold_project.models[0].query_sql - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="corrupt cache fallback", expected_count=2),), - ids=lambda case: case.description, -) -def test_given_corrupt_analysis_when_compiling_then_reanalyzes_and_repairs_the_entry( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = compiler_cache_directory(tmp_path) / "v13" / "model-analysis.sqlite3" - with sqlite3.connect(cache_path) as connection: - persisted_contents: str = connection.execute( - "SELECT payload FROM model_analysis" - ).fetchone()[0] - corrupt_contents: str = persisted_contents.replace("order_id", "tampered", 1) - assert corrupt_contents != persisted_contents - _ = connection.execute( - "UPDATE model_analysis SET payload = ?", - (corrupt_contents,), - ) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - repaired_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - with sqlite3.connect(cache_path) as connection: - repaired_contents: str = connection.execute( - "SELECT payload FROM model_analysis" - ).fetchone()[0] - _digest, _separator, serialized_payload = repaired_contents.partition("\n") - repaired_payload: dict[str, object] = json.loads(serialized_payload) - assert repaired_project.models == cold_project.models - assert repaired_payload["v"] == 13 - assert isinstance(repaired_payload["s"], str) - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="non-text analysis cache fallback", expected_count=2),), - ids=lambda case: case.description, -) -def test_given_non_text_analysis_cache_when_compiling_then_reanalyzes_safely( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = compiler_cache_directory(tmp_path) / "v13" / "model-analysis.sqlite3" - with sqlite3.connect(cache_path) as connection: - _ = connection.execute( - "UPDATE model_analysis SET payload = ?", - (sqlite3.Binary(b"broken"),), - ) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="corrupt reference cache fallback", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_corrupt_reference_cache_when_compiling_then_rescans_and_repairs_the_entry( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - cold_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = next( - (compiler_cache_directory(tmp_path) / "references-v3").glob("*.sqlite3") - ) - with sqlite3.connect(cache_path) as connection: - persisted_contents: str = connection.execute( - "SELECT payload FROM sql_reference" - ).fetchone()[0] - corrupt_contents: str = persisted_contents.replace("raw_orders", "tampered", 1) - assert corrupt_contents != persisted_contents - _ = connection.execute( - "UPDATE sql_reference SET payload = ?", - (corrupt_contents,), - ) - scanner: Mock = Mock(wraps=reference_cache.scan_sql_reference_calls) - monkeypatch.setattr(reference_cache, "scan_sql_reference_calls", scanner) - - repaired_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - - with sqlite3.connect(cache_path) as connection: - repaired_contents: str = connection.execute("SELECT payload FROM sql_reference").fetchone()[ - 0 - ] - assert repaired_project.models == cold_project.models - assert "raw_orders" in repaired_contents - assert scanner.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="non-text reference cache fallback", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_non_text_reference_cache_when_compiling_then_rescans_safely( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - cache_path: Path = next( - (compiler_cache_directory(tmp_path) / "references-v3").glob("*.sqlite3") - ) - with sqlite3.connect(cache_path) as connection: - _ = connection.execute( - "UPDATE sql_reference SET payload = ?", - (sqlite3.Binary(b"broken"),), - ) - scanner: Mock = Mock(wraps=reference_cache.scan_sql_reference_calls) - monkeypatch.setattr(reference_cache, "scan_sql_reference_calls", scanner) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert scanner.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="deterministic unsuccessful result", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_unsuccessful_analysis_when_writing_then_reuses_deterministic_result( - test_case: AnalysisCacheTestCase, - tmp_path: Path, -) -> None: - cache_key: str = "a" * 64 - context: AnalysisCacheContext = AnalysisCacheContext( - root=tmp_path, - shared_fingerprint="shared", - ) - write_model_analyses( - context=context, - analyses_by_key={ - cache_key: PolyglotAnalysisResult(analysis_succeeded=False), - }, - latest_analyses_by_model={ - "orders": PolyglotAnalysisResult(analysis_succeeded=False), - }, - ) - analyses, _, _ = read_model_analyses( - context=context, - cache_keys=(cache_key,), - model_names=("orders",), - upstream_model_names_by_key={cache_key: ()}, - ) - - assert len(analyses) == test_case.expected_count - assert analyses[cache_key].analysis_succeeded is False - - -@pytest.mark.parametrize( - "test_case", - ( - CachedLineageRoundTripTestCase( - description="indexed lineage decodes lazily", - transform_kind=ColumnTransformKind.CAST, - confidence=ColumnLineageConfidence.HIGH, - expected_compact=True, - ), - CachedLineageRoundTripTestCase( - description="unindexed lineage values decode eagerly", - transform_kind=ColumnTransformKind.UNKNOWN, - confidence=ColumnLineageConfidence.LOW, - expected_compact=False, - ), - ), - ids=lambda case: case.description, -) -def test_given_cached_lineage_when_reading_then_facts_round_trip_exactly( - test_case: CachedLineageRoundTripTestCase, - tmp_path: Path, -) -> None: - cache_key: str = "b" * 64 - context: AnalysisCacheContext = AnalysisCacheContext(root=tmp_path, shared_fingerprint="shared") - shared_source: CompiledLineageSourceFact = CompiledLineageSourceFact( - resource_type="source", resource_name="raw_orders", column_name="order_id" - ) - analysis: PolyglotAnalysisResult = PolyglotAnalysisResult( - analysis_succeeded=True, - lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=(shared_source,), - transform_kind=test_case.transform_kind, - confidence=test_case.confidence, - ), - CompiledLineageColumnFact( - output_column="order_key", - upstream_columns=( - shared_source, - CompiledLineageSourceFact( - resource_type="model", resource_name="orders", column_name="order_key" - ), - ), - transform_kind=ColumnTransformKind.EXPRESSION, - confidence=ColumnLineageConfidence.MEDIUM, - ), - ), - ) - write_model_analyses( - context=context, - analyses_by_key={cache_key: analysis}, - latest_analyses_by_model={"orders": analysis}, - ) - - analyses, _, _ = read_model_analyses( - context=context, - cache_keys=(cache_key,), - model_names=("orders",), - upstream_model_names_by_key={cache_key: ()}, - ) - - assert analyses[cache_key] == analysis - assert tuple(analyses[cache_key].lineage_columns) == analysis.lineage_columns - assert ( - isinstance(analyses[cache_key].lineage_columns, CompactLineageFacts) - is test_case.expected_compact - ) - - -@pytest.mark.parametrize( - "test_case", - ( - CompactLineageCacheEncodingTestCase( - description="canonical relation names", - output_column="order_id", - resource_names={}, - ), - CompactLineageCacheEncodingTestCase( - description="shared result projected onto member relation names", - output_column='caf\u00e9 "note"\n\t\x01\u2028', - resource_names={4: "orders_archive"}, - ), - ), - ids=lambda case: case.description, -) -def test_given_compact_lineage_when_writing_then_entry_matches_projected_fact_encoding( - test_case: CompactLineageCacheEncodingTestCase, - tmp_path: Path, -) -> None: - compact: CompactLineageFacts = CompactLineageFacts( - string_pool=("source", "raw_orders", test_case.output_column, "model", "orders"), - rows=( - (2, 1, 1, ((0, 1, 2),)), - (2, 2, 2, ((0, 1, 2), (3, 4, 2))), - ), - resource_names=test_case.resource_names, - ) - compact_analysis: PolyglotAnalysisResult = PolyglotAnalysisResult( - analysis_succeeded=True, lineage_columns=compact - ) - projected_analysis: PolyglotAnalysisResult = PolyglotAnalysisResult( - analysis_succeeded=True, lineage_columns=tuple(compact) - ) - compact_context: AnalysisCacheContext = AnalysisCacheContext( - root=tmp_path / "compact", shared_fingerprint="shared" - ) - projected_context: AnalysisCacheContext = AnalysisCacheContext( - root=tmp_path / "projected", shared_fingerprint="shared" - ) - cache_key: str = "c" * 64 - - write_model_analyses(context=compact_context, analyses_by_key={cache_key: compact_analysis}) - write_model_analyses(context=projected_context, analyses_by_key={cache_key: projected_analysis}) - - compact_contents: str = stored_analysis_contents(context=compact_context, cache_key=cache_key) - serialized_payload: str = compact_contents.partition("\n")[2] - assert compact_contents == stored_analysis_contents( - context=projected_context, cache_key=cache_key - ) - assert serialized_payload == json.dumps( - json.loads(serialized_payload), ensure_ascii=False, separators=(",", ":"), sort_keys=True - ) - analyses, _, _ = read_model_analyses( - context=compact_context, - cache_keys=(cache_key,), - model_names=(), - upstream_model_names_by_key={cache_key: ()}, - ) - assert tuple(analyses[cache_key].lineage_columns) == tuple(compact) - assert len(analyses[cache_key].lineage_columns) == test_case.expected_lineage_columns - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="stable per-model digests", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_model_payload_when_digesting_then_fast_digest_matches_stable_encoding( - test_case: AnalysisCacheTestCase, -) -> None: - payload: dict[str, object] = { - "query_sql": "SELECT \"caf\u00e9\" AS name, '\\n\t\x01\u2028' AS note", - "references": [{"kind": "ref", "name": "orders", "package": None, "count": 2}], - "placeholders": {"b": "2", "a": "1"}, - "upstream": (("orders", "a" * 64),), - "binding_schema": None, - "has_star": False, - } - - digests: set[str] = { - analysis_cache._json_value_digest(payload), - analysis_cache._payload_digest(payload), - } - - assert len(digests) == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="semantic cache identities", expected_count=7),), - ids=lambda case: case.description, -) -def test_given_analysis_inputs_when_building_keys_then_all_semantic_inputs_affect_identity( - test_case: AnalysisCacheTestCase, - tmp_path: Path, -) -> None: - base_context: AnalysisCacheContext | None = build_analysis_cache_context( - root=tmp_path, - inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb"), - allow_compact_analysis=False, - ) - profile_context: AnalysisCacheContext | None = build_analysis_cache_context( - root=tmp_path, - inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="snowflake"), - allow_compact_analysis=True, - ) - return_type_context: AnalysisCacheContext | None = build_analysis_cache_context( - root=tmp_path, - inference_profile=ExpressionInferenceProfile( - sql_analysis_dialect="duckdb", function_return_types={"TO_VARIANT": "VARIANT"} - ), - allow_compact_analysis=False, - ) - assert base_context is not None - assert profile_context is not None - assert return_type_context is not None - reference: CompileSqlReference = CompileSqlReference( - ref_kind=SqlReferenceKind.SOURCE, - ref_name="raw_orders", - call_argument_count=1, - ) - changed_reference: CompileSqlReference = CompileSqlReference( - ref_kind=SqlReferenceKind.SOURCE, - ref_name="raw_customers", - call_argument_count=1, - ) - base_key: str = model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - ) - - changed_keys: tuple[str, ...] = ( - model_analysis_cache_key( - context=base_context, - query_sql="SELECT 2", - references=(reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(changed_reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(reference,), - placeholders={"value": "1"}, - column_nullability_by_table={}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={"raw_orders": {"order_id": InferredNullability.NON_NULL}}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=profile_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=return_type_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - ), - model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={}, - column_types_by_table={}, - recover_cte_facts=True, - ), - ) - unrelated_schema_key: str = model_analysis_cache_key( - context=base_context, - query_sql="SELECT 1", - references=(reference,), - placeholders=None, - column_nullability_by_table={"unrelated": {"order_id": InferredNullability.NON_NULL}}, - column_types_by_table={}, - ) - - assert len(changed_keys) == test_case.expected_count - assert all(changed_key != base_key for changed_key in changed_keys) - assert unrelated_schema_key == base_key - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="propagated schema invalidation", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_schema_change_when_compiling_then_affected_consumers_miss_cache( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files( - tmp_path, - { - **_SELECTION_REPO_FILES, - "models/root.sql": "MODEL (description 'Test model root.');\n\nSELECT CAST(NULL AS INTEGER) AS id\n", - }, - ) - _ = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - unrelated_path: Path = tmp_path / "models" / "unrelated.sql" - unrelated_path.write_text( - unrelated_path.read_text(encoding="utf-8").replace( - "MODEL (description 'Test model unrelated.');", - "MODEL (description 'Test model unrelated.', columns (id (nullable false)));", - 1, - ), - encoding="utf-8", - ) - - _ = compile_project_with_cache(project_dir=tmp_path) - - analyzer.assert_not_called() - root_path: Path = tmp_path / "models" / "root.sql" - root_path.write_text( - root_path.read_text(encoding="utf-8").replace( - "MODEL (description 'Test model root.');", - "MODEL (description 'Test model root.', columns (id (nullable false)));", - 1, - ), - encoding="utf-8", - ) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - _ = compile_project_with_cache(project_dir=tmp_path) - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="stable exported signature", expected_count=1),), - ids=lambda case: case.description, -) -def test_given_model_sql_change_with_stable_signature_when_compiling_then_downstream_hits_cache( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - (tmp_path / "models" / "root.sql").write_text( - "MODEL (description 'Test model root.');\n\nSELECT 3 AS id\n", - encoding="utf-8", - ) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="changed exported signature", expected_count=5),), - ids=lambda case: case.description, -) -def test_given_model_output_change_when_compiling_then_downstream_closure_misses_cache( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - (tmp_path / "models" / "root.sql").write_text( - "MODEL (description 'Test model root.');\n\nSELECT 1 AS id, 2 AS extra\n", - encoding="utf-8", - ) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - _ = compile_project_with_cache(project_dir=tmp_path) - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="parallel downstream invalidation", expected_count=2),), - ids=lambda case: case.description, -) -def test_given_changed_output_signature_when_reanalyzing_downstream_then_uses_dependency_ready_batches( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - root_path: Path = tmp_path / "models" / "root.sql" - original_sql: str = root_path.read_text(encoding="utf-8") - root_path.write_text( - "MODEL (description 'Test model root.');\n\nSELECT 1 AS id, 2 AS extra\n", encoding="utf-8" - ) - _ = compile_project_with_cache(project_dir=tmp_path) - batcher: Mock = Mock(wraps=assembly_project.analyze_queries_with_compact_polyglot_batch) - monkeypatch.setattr(assembly_project, "analyze_queries_with_compact_polyglot_batch", batcher) - root_path.write_text(original_sql, encoding="utf-8") - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert batcher.call_count == test_case.expected_count - assert all(len(call.kwargs["query_sqls"]) == 1 for call in batcher.call_args_list) - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="restored exported signature", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_cached_model_signature_is_restored_when_compiling_then_downstream_reanalyzes( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - _ = compile_project_with_cache(project_dir=tmp_path) - root_path: Path = tmp_path / "models" / "root.sql" - original_sql: str = root_path.read_text(encoding="utf-8") - root_path.write_text( - "MODEL (description 'Test model root.');\n\nSELECT 1 AS id, 2 AS extra\n", encoding="utf-8" - ) - _ = compile_project_with_cache(project_dir=tmp_path) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - root_path.write_text(original_sql, encoding="utf-8") - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="selected upstream change", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_selected_upstream_change_when_compiling_full_project_then_stale_consumer_misses( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files( - tmp_path, - { - **_SELECTION_REPO_FILES, - "models/middle.sql": 'MODEL (description "Test model middle.");\n\nSELECT * FROM __ref("root")\n', - "models/leaf.sql": 'MODEL (description "Test model leaf.");\n\nSELECT * FROM __ref("middle")\n', - }, - ) - _ = compile_project_with_cache(project_dir=tmp_path) - (tmp_path / "models" / "root.sql").write_text( - "MODEL (description 'Test model root.');\n\nSELECT 1 AS id, 2 AS extra\n", - encoding="utf-8", - ) - _ = compile_project_with_cache( - project_dir=tmp_path, - analysis_selection=CompileAnalysisSelection(select=("root",)), - ) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="compact upstream interface change", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_upstream_interface_change_when_compact_batch_exists_then_dependents_reanalyze( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files( - tmp_path, - { - **_SELECTION_REPO_FILES, - "models/middle.sql": 'MODEL (description "Test model middle.");\n\nSELECT * FROM __ref("root")\n', - "models/leaf.sql": 'MODEL (description "Test model leaf.");\n\nSELECT * FROM __ref("middle")\n', - }, - ) - monkeypatch.setattr(assembly_project, "_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT", 1) - _ = compile_project_with_cache(project_dir=tmp_path) - (tmp_path / "models" / "root.sql").write_text( - "MODEL (description 'Test model root.');\n\nSELECT 1 AS id, 2 AS extra\n", - encoding="utf-8", - ) - - with collect_compile_timings() as metrics: - changed_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - expected_project: CompiledProject = compile_project_with_cache( - project_dir=tmp_path, - no_cache=True, - ) - - models_by_name: dict[str, CompiledModel] = { - model.name: model for model in changed_project.models - } - expected_models_by_name: dict[str, CompiledModel] = { - model.name: model for model in expected_project.models - } - assert ( - models_by_name["root"].inferred_columns == expected_models_by_name["root"].inferred_columns - ) - assert models_by_name["middle"].inferred_columns == ( - expected_models_by_name["middle"].inferred_columns - ) - assert ( - models_by_name["leaf"].inferred_columns == expected_models_by_name["leaf"].inferred_columns - ) - recorded: dict[str, int] = metrics.as_milliseconds() - assert ( - sum( - recorded[name] - for name in ( - "analysis_batch_cache_hits", - "analysis_entry_cache_hits", - "analysis_cache_misses", - ) - ) - == test_case.expected_count - ) - assert recorded["analysis_batch_cache_hits"] == 1 - assert recorded["analysis_entry_cache_hits"] == 2 - assert recorded["analysis_cache_misses"] == 1 - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="selected upstream analysis", expected_count=3),), - ids=lambda case: case.description, -) -def test_given_partial_selection_when_compiling_then_analyzes_only_upstream_closure( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _SELECTION_REPO_FILES) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - project: CompiledProject = compile_project_with_cache( - project_dir=tmp_path, - analysis_selection=CompileAnalysisSelection(select=("middle",)), - ) - - inferred_by_name: dict[str, bool] = { - model.name: model.inferred_columns is not None for model in project.models - } - assert analyzer.call_count == test_case.expected_count - assert inferred_by_name == { - "leaf": False, - "middle": True, - "root": True, - "unrelated": False, - } - full_project: CompiledProject = compile_project_with_cache(project_dir=tmp_path) - full_models_by_name: dict[str, CompiledModel] = { - model.name: model for model in full_project.models - } - selected_models_by_name: dict[str, CompiledModel] = { - model.name: model for model in project.models - } - assert selected_models_by_name["root"] == full_models_by_name["root"] - assert selected_models_by_name["middle"] == full_models_by_name["middle"] - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="unselected invalid reference", expected_count=0),), - ids=lambda case: case.description, -) -def test_given_unselected_invalid_reference_when_compiling_then_live_validation_still_fails( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - repo_files: dict[str, str] = _SELECTION_REPO_FILES | { - "models/unrelated.sql": 'MODEL (description "Test model unrelated.");\n\nSELECT id FROM __ref("missing")\n' - } - write_repo_files(tmp_path, repo_files) - - with pytest.raises(CompileInputError, match="references unknown model"): - _ = compile_project_with_cache( - project_dir=tmp_path, - analysis_selection=CompileAnalysisSelection(select=("middle",)), - ) - - assert len(tuple((tmp_path / "target").rglob("*.sqlite3"))) == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="explicit cache bypass", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_compile_cache_bypass_when_compiling_twice_then_both_runs_analyze_cold( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - monkeypatch.setenv(COMPILE_CACHE_DISABLE_ENV_VAR, "1") - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - batch_plan_spy: Mock = Mock(wraps=assembly_project.build_compact_analysis_cache_plan) - monkeypatch.setattr( - assembly_project, - "build_compact_analysis_cache_plan", - batch_plan_spy, - ) - - _ = compile_project_with_cache(project_dir=tmp_path) - with collect_compile_timings() as metrics: - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - assert batch_plan_spy.call_count == 0 - assert not tuple((tmp_path / "target").rglob("*.sqlite3")) - recorded: dict[str, int] = metrics.as_milliseconds() - assert recorded["analysis_batch_cache_hits"] == 0 - assert recorded["analysis_entry_cache_hits"] == 0 - assert recorded["analysis_cache_misses"] == 0 - assert recorded["analysis_cache_bypasses"] == 1 - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="command cache bypass", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_command_cache_bypass_when_compiling_twice_then_both_runs_analyze_cold( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files(tmp_path, _CACHE_REPO_FILES) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - _ = compile_project_with_cache(project_dir=tmp_path, no_cache=True) - _ = compile_project_with_cache(project_dir=tmp_path, no_cache=True) - - assert analyzer.call_count == test_case.expected_count - assert not tuple((tmp_path / "target").rglob("*.sqlite3")) - - -@pytest.mark.parametrize( - "test_case", - (AnalysisCacheTestCase(description="target cache bypass", expected_count=4),), - ids=lambda case: case.description, -) -def test_given_target_cache_disabled_when_compiling_twice_then_both_runs_analyze_cold( - test_case: AnalysisCacheTestCase, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, - write_repo_files: Callable[[Path, dict[str, str]], None], -) -> None: - write_repo_files( - tmp_path, - { - **_CACHE_REPO_FILES, - "sqlbuild_project.toml": """ -name = "cache_demo" -adapter = "duckdb" -default_target = "prod" - -[targets.prod] -compile_cache = false -""".strip() - + "\n", - }, - ) - analyzer: Mock = Mock(wraps=assembly_project.analyze_columns_and_lineage_with_polyglot) - monkeypatch.setattr(assembly_project, "analyze_columns_and_lineage_with_polyglot", analyzer) - - _ = compile_project_with_cache(project_dir=tmp_path) - _ = compile_project_with_cache(project_dir=tmp_path) - - assert analyzer.call_count == test_case.expected_count - assert not tuple((tmp_path / "target").rglob("*.sqlite3")) From 1cbb2e5f72c0b185d9f4afa36da3b1104111e1db Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:58:07 +0100 Subject: [PATCH 11/36] feat(native): fail FAST lineage on internal native failures without the wheel A parser panic or unreadable tree in native FAST lineage raises NativeCompilerError instead of re-parsing with the Polyglot wheel, which aborts the process on the same input; invalid SQL keeps its unparsed log. Deletes the per-model Python FAST builder. Analysis-session enrichment no longer maps an analyze_query options rejection to Python's ValueError: compile passes the alias-mapped dialect (postgres -> postgresql), so the rejection is internal. --- .../analysis_session/_helpers/enrichment.rs | 11 +- .../analysis_session/_helpers/session.rs | 4 - .../assembly/analysis_session/constants.rs | 2 - .../tests/test_analysis_cache.rs | 2 +- .../tests/test_analysis_session.rs | 11 +- .../src/lineage/_helpers/parsed_lineage.rs | 32 +- .../src/lineage/main/build_fast_lineage.rs | 31 +- .../sqlbuild-analysis/src/lineage/models.rs | 24 +- .../src/lineage/tests/helpers.rs | 7 +- .../lineage/tests/test_build_fast_lineage.rs | 9 +- crates/sqlbuild-core/src/constants.rs | 2 - .../_helpers/analysis_session/session.rs | 9 +- scripts/compiler_differential/constants.py | 1 - .../compiler/lineage/_helpers/fast_columns.py | 464 ------------------ .../lineage/_helpers/native_deferrals.py | 26 - .../lineage/_helpers/native_fast_columns.py | 14 - src/sqlbuild/compiler/lineage/constants.py | 2 - .../main/compile/fast_lineage/helpers.py | 22 +- .../fast_lineage/test_native_fast_lineage.py | 1 - .../sqlbuild/compiler/lineage/_test_types.py | 10 + .../lineage/test_native_fast_lineage.py | 38 ++ 21 files changed, 114 insertions(+), 608 deletions(-) delete mode 100644 src/sqlbuild/compiler/lineage/_helpers/fast_columns.py delete mode 100644 src/sqlbuild/compiler/lineage/_helpers/native_deferrals.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index c96e636714..038c4c0ca6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -4,7 +4,6 @@ use std::collections::{HashMap, HashSet}; use polyglot_sql::{AnalyzeQueryOptions, analyze_query}; use serde_json::{Map, Value, json}; -use sqlbuild_core::constants::PYTHON_VALUE_ERROR; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::analysis_session::_helpers::cte_facts::{ @@ -15,10 +14,10 @@ use crate::assembly::analysis_session::_helpers::dict_walk::{truthy, upper}; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; use crate::assembly::analysis_session::constants::{ CAST_TRANSFORM, CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, FILTER_CONTEXT, - INVALID_ANALYZE_OPTIONS, MAX_FUNCTION_CALL_DEPTH, NON_NULL_NULLABILITY, NULL_KEYWORD, - RESOLVED_SOURCE_CONFIDENCE, SELECT_SHAPE, SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, - TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, - UNKNOWN_NULLABILITY, UNKNOWN_TYPE, WILDCARD, + MAX_FUNCTION_CALL_DEPTH, NON_NULL_NULLABILITY, NULL_KEYWORD, RESOLVED_SOURCE_CONFIDENCE, + SELECT_SHAPE, SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, + TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, + WILDCARD, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow, ModelRequest}; use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; @@ -84,7 +83,7 @@ fn analysed(input: &EnrichmentInput<'_>) -> Result { json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH}), ); let options: AnalyzeQueryOptions = serde_json::from_value(Value::Object(options)) - .map_err(|error| format!("{PYTHON_VALUE_ERROR}{INVALID_ANALYZE_OPTIONS}{error}"))?; + .map_err(|error| format!("query analysis options: {error}"))?; let analysis = match analyze_query(&cleaned, options) { Ok(analysis) => analysis, Err(_) => return legacy(input, &cleaned), diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index e95449cd89..2106569b1c 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -7,7 +7,6 @@ use rayon::ThreadPool; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; -use sqlbuild_core::constants::PYTHON_VALUE_ERROR; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; @@ -363,9 +362,6 @@ impl AnalysisSession { .zip(&inputs) .map(|(enrichment, input)| { enrichment.map_err(|reason| { - if reason.starts_with(PYTHON_VALUE_ERROR) { - return reason; - } native_failure( &format!("{} enrichment", model_context(input.model)), &reason, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index 9356af92f7..a312ce04a6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -6,8 +6,6 @@ pub(crate) const LEGACY_FALLBACK: &str = "native project type recovery requires pub(crate) const INVALID_NATIVE_RESPONSE: &str = "invalid native response"; /// Python's `UNKNOWN` column type. pub(crate) const UNKNOWN_TYPE: &str = "UNKNOWN"; -/// The wheel's `analyze_query` message for options it cannot read, such as a dialect alias. -pub(crate) const INVALID_ANALYZE_OPTIONS: &str = "Invalid analyze_query options object: "; /// Python's `InferredNullability.UNKNOWN`. pub(crate) const UNKNOWN_NULLABILITY: &str = "unknown"; /// Python's `InferredNullability` values by native code. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs index c08f2a556c..24c55a4b51 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_cache.rs @@ -354,7 +354,7 @@ fn given_any_analysis_input_changed_when_keying_then_the_key_changes() { }, CacheKeyTestCase { description: "dialect", - change: |request| request.dialect = "snowflake".to_owned(), + change: |request| request.dialect = "postgresql".to_owned(), expected_key_changed: true, }, CacheKeyTestCase { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 3a5f6069de..b66ef69372 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -299,20 +299,21 @@ fn given_models_when_starting_then_only_duplicate_names_fail_as_an_internal_erro } #[test] -fn given_enrichment_dialect_when_running_then_answers_or_raises_python_value_error() { +fn given_enrichment_dialect_when_running_then_answers_natively_or_fails_internally() { let test_cases = [ SessionFailureTestCase { - description: "an analysis dialect name the query analysis reads", - dialect: "duckdb", + description: "the dialect compile passes for the postgres adapter", + dialect: "postgresql", models: &[UNTYPED_ORDERS], expected_error: None, }, SessionFailureTestCase { - description: "an adapter dialect alias Python's analyze_query options reject", + description: "an adapter alias compile maps before analysis never reaches the session", dialect: "postgres", models: &[UNTYPED_ORDERS], expected_error: Some( - "ValueError: Invalid analyze_query options object: unknown variant `postgres`", + "NativeCompilerError: native model analysis of model 'orders_untyped' enrichment: \ + query analysis options: unknown variant `postgres`", ), }, ]; diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs index 252bed3b64..fc4bb88769 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs @@ -12,8 +12,8 @@ use crate::lineage::_helpers::references::{PhysicalResource, normalized_sql, phy use crate::lineage::_helpers::stars::star_lineage; use crate::lineage::constants::{KIND_SELECT, KIND_UNION, MAX_FUNCTION_CALL_DEPTH}; use crate::lineage::models::{ - FastLineageOutcome, LineageColumn, LineageConfidence, LineageDeferral, LineageResourceType, - LineageSource, LineageTransformKind, + FastLineageOutcome, LineageColumn, LineageConfidence, LineageResourceType, LineageSource, + LineageTransformKind, }; /// The parse options SQLBuild's Polyglot proxy gives `parse_one`, decoded as the wheel does. @@ -35,25 +35,31 @@ pub(crate) struct ParsedModel<'a> { pub(crate) options: &'a Result, } -pub(crate) fn parsed_model_lineage(model: &ParsedModel<'_>) -> FastLineageOutcome { +/// The model's lineage outcome. +/// +/// # Errors +/// +/// An internal native failure: a tree the lineage walk cannot read. +pub(crate) fn parsed_model_lineage(model: &ParsedModel<'_>) -> Result { let dialect: DialectType = match model.dialect { Ok(dialect) => dialect, - Err(name) => return FastLineageOutcome::UnknownDialect(name.to_owned()), - }; - let Ok(options) = model.options else { - return FastLineageOutcome::Deferred(LineageDeferral::NativeFailure); + Err(name) => return Ok(FastLineageOutcome::UnknownDialect(name.to_owned())), }; + let options: &ParseOptions = model + .options + .as_ref() + .map_err(|_| "the parse options do not build".to_owned())?; let physical = physical_resources(model.query_sql); let sql = normalized_sql(model.query_sql); let mut statements = match Dialect::get(dialect).parse_with_options(&sql, options) { Ok(statements) => statements, - Err(error) => return FastLineageOutcome::Unparsed(error.to_string()), + Err(error) => return Ok(FastLineageOutcome::Unparsed(error.to_string())), }; if statements.len() != 1 { - return FastLineageOutcome::Unparsed(format!( + return Ok(FastLineageOutcome::Unparsed(format!( "Expected 1 statement, found {}", statements.len() - )); + ))); } let parsed = statements.remove(0); let built = match parsed.variant_name() { @@ -62,9 +68,9 @@ pub(crate) fn parsed_model_lineage(model: &ParsedModel<'_>) -> FastLineageOutcom _ => Ok(None), }; match built { - Ok(Some((columns, has_star))) => FastLineageOutcome::Built { columns, has_star }, - Ok(None) => FastLineageOutcome::Omitted, - Err(UnreadableExpression) => FastLineageOutcome::Deferred(LineageDeferral::NativeFailure), + Ok(Some((columns, has_star))) => Ok(FastLineageOutcome::Built { columns, has_star }), + Ok(None) => Ok(FastLineageOutcome::Omitted), + Err(UnreadableExpression) => Err("a parsed tree the lineage walk cannot read".to_owned()), } } diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index 2aabc91905..d5685f5dd8 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -6,6 +6,7 @@ use polyglot_sql::ParseOptions; use rayon::ThreadPool; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; use crate::lineage::_helpers::parsed_lineage::{ ParsedModel, parsed_model_lineage, proxy_parse_options, @@ -13,9 +14,7 @@ use crate::lineage::_helpers::parsed_lineage::{ use crate::lineage::_helpers::references::physical_resources; use crate::lineage::_helpers::stars::{schema_mapping, star_lineage}; use crate::lineage::main::parser_dialect::parser_dialect; -use crate::lineage::models::{ - FastLineageModel, FastLineageOutcome, FastLineageRequest, LineageDeferral, -}; +use crate::lineage::models::{FastLineageModel, FastLineageOutcome, FastLineageRequest}; use crate::semantic_validation::_helpers::catalog::new_analysis_pool; use crate::semantic_validation::models::ProjectCatalog; @@ -29,11 +28,24 @@ pub fn build_fast_lineage( Some(catalog) => catalog.analysis_pool()?, None => Arc::new(new_analysis_pool()?), }; - Ok(pool.install(|| model_outcomes(request))) + let outcomes: Vec> = + pool.install(|| model_outcomes(request)); + outcomes + .into_iter() + .enumerate() + .map(|(index, outcome)| { + outcome.map_err(|reason| { + native_failure( + &format!("native fast lineage of request model {index}"), + &reason, + ) + }) + }) + .collect() } -/// A parser panic defers only its own model. -fn model_outcomes(request: &FastLineageRequest) -> Vec { +/// Each model's outcome or internal failure, panics included, in request order. +fn model_outcomes(request: &FastLineageRequest) -> Vec> { let schema = schema_mapping(&request.schema); let name: &str = request .dialect @@ -49,11 +61,11 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec { FastLineageModel::StarExpansion { query_sql, existing_columns, - } => FastLineageOutcome::StarColumns(star_lineage( + } => Ok(FastLineageOutcome::StarColumns(star_lineage( &schema, &physical_resources(query_sql), existing_columns.iter().map(String::as_str), - )), + ))), FastLineageModel::Parse { query_sql, inferred_columns, @@ -65,8 +77,7 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec { dialect, options: &options, }; - catch_compiler_panic(|| Ok(parsed_model_lineage(&model))) - .unwrap_or(FastLineageOutcome::Deferred(LineageDeferral::NativeFailure)) + catch_compiler_panic(|| parsed_model_lineage(&model)) } }) .collect() diff --git a/crates/sqlbuild-analysis/src/lineage/models.rs b/crates/sqlbuild-analysis/src/lineage/models.rs index 8f82026308..f4821c90fb 100644 --- a/crates/sqlbuild-analysis/src/lineage/models.rs +++ b/crates/sqlbuild-analysis/src/lineage/models.rs @@ -118,21 +118,6 @@ pub struct LineageColumn { pub upstream_columns: Vec, } -/// Why a model is handed back to Python, which builds its lineage exactly as before. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum LineageDeferral { - /// The parsed SQL could not be read as Python reads it. - NativeFailure, -} - -impl LineageDeferral { - pub fn as_str(self) -> &'static str { - match self { - Self::NativeFailure => "native_failure", - } - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum FastLineageOutcome { /// Star columns to append to the model's compact facts. @@ -148,11 +133,10 @@ pub enum FastLineageOutcome { Unparsed(String), /// Polyglot does not know the dialect name, so Python's parse raises `ValueError`. UnknownDialect(String), - Deferred(LineageDeferral), } impl FastLineageOutcome { - /// `(status, columns, has_star, detail)`: detail is the parse error or the deferral kind. + /// `(status, columns, has_star, detail)`: detail is the parse error or the unknown dialect. pub fn into_parts(self) -> (&'static str, Vec, bool, Option) { match self { Self::StarColumns(columns) => ("star", columns, true, None), @@ -160,12 +144,6 @@ impl FastLineageOutcome { Self::Omitted => ("omitted", Vec::new(), false, None), Self::Unparsed(message) => ("unparsed", Vec::new(), false, Some(message)), Self::UnknownDialect(name) => ("unknown_dialect", Vec::new(), false, Some(name)), - Self::Deferred(kind) => ( - "deferred", - Vec::new(), - false, - Some(kind.as_str().to_owned()), - ), } } } diff --git a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs index b5699ce9ba..fc235a3955 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs @@ -21,9 +21,10 @@ pub(crate) fn outcome_lines( schema: schema(), models: vec![model], }; - let outcomes: Vec = - build_fast_lineage(&request, Some(&catalog())).expect("the analysis pool builds"); - outcomes.into_iter().map(described_outcome).collect() + match build_fast_lineage(&request, Some(&catalog())) { + Ok(outcomes) => outcomes.into_iter().map(described_outcome).collect(), + Err(error) => vec![("error", Vec::new(), false, Some(error))], + } } /// A `branches`-deep `UNION ALL` chain built from a caller thread with a `stack_bytes` stack. diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index 424b3318fa..a384f09acf 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -196,14 +196,17 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( expected_detail: Some("Expected 1 statement, found 2"), }, ParsedLineageTestCase { - description: "a parser panic on truncated T-SQL defers only that model", + description: "a parser panic on truncated T-SQL fails as an internal error", sql: "SELECT IF(region > 1, re", dialect: Some("tsql"), inferred_columns: &[], - expected_status: "deferred", + expected_status: "error", expected_lines: &[], expected_has_star: false, - expected_detail: Some("native_failure"), + expected_detail: Some( + "NativeCompilerError: native SQL compilation panicked \ + (native fast lineage of request model 0)", + ), }, ParsedLineageTestCase { description: "a dialect outside the old parser build is parsed natively", diff --git a/crates/sqlbuild-core/src/constants.rs b/crates/sqlbuild-core/src/constants.rs index d0ab0de4b6..cfb324eb8c 100644 --- a/crates/sqlbuild-core/src/constants.rs +++ b/crates/sqlbuild-core/src/constants.rs @@ -6,5 +6,3 @@ pub const CLOSE_PAREN: &str = ")"; pub const PANIC_MESSAGE: &str = "NativeCompilerError: native SQL compilation panicked"; /// The prefix every native internal failure carries, so the boundary raises `NativeCompilerError`. pub const NATIVE_FAILURE_PREFIX: &str = "NativeCompilerError: "; -/// The prefix of a native failure Python raised as `ValueError`, with the rest as its message. -pub const PYTHON_VALUE_ERROR: &str = "ValueError: "; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 137b2e1c8b..3a9fbaf480 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -1,6 +1,5 @@ //! The native model analysis session and expression-source shapes for the preview engine. -use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, Py, PyAny, PyErr, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::types::PyDict; use pyo3::{pyclass, pyfunction, pymethods, wrap_pyfunction}; @@ -24,7 +23,7 @@ use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; use sqlbuild_analysis::semantic_validation::types::DiagnosticRow; use sqlbuild_cache::store::models::NativeStore; -use sqlbuild_core::constants::{PANIC_MESSAGE, PYTHON_VALUE_ERROR}; +use sqlbuild_core::constants::PANIC_MESSAGE; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::is_compiler_panic::is_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; @@ -143,12 +142,8 @@ impl NativeModelAnalysisSession { } } -/// An internal native failure of the session as `NativeCompilerError`, or the `ValueError` -/// Python's analysis raised for the same input. +/// An internal native failure of the session as `NativeCompilerError`. fn internal_error(reason: &str) -> PyErr { - if let Some(message) = reason.strip_prefix(PYTHON_VALUE_ERROR) { - return PyValueError::new_err(message.to_owned()); - } if is_compiler_panic(reason) && reason != PANIC_MESSAGE { return compiler_error(reason); } diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index 1581a6a3ef..d8ad31da50 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -26,7 +26,6 @@ NATIVE_FALLBACK_PROJECT_CORPUS: str = "project" NATIVE_FALLBACK_DEFERRAL_STAGES: dict[str, str] = { "analysis_session": "model_analysis", - "fast_columns.py": "lineage_facts", "type_recovery.py": "semantic_checks", "recovery.py": "semantic_checks", "metadata_validation.py": "semantic_checks", diff --git a/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py b/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py deleted file mode 100644 index d70a7b25c4..0000000000 --- a/src/sqlbuild/compiler/lineage/_helpers/fast_columns.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Python fast lineage of one model, for models the native engine hands back.""" - -from __future__ import annotations - -import logging -from typing import Any, cast - -from sqlbuild.compiler.compile.models import ( - CompiledModel, -) -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.lineage._helpers.columns import ( - _build_star_lineage, - _normalize_sqlbuild_refs, -) -from sqlbuild.compiler.lineage.constants import STAR_COLUMN_NAME -from sqlbuild.compiler.lineage.models import ( - ColumnLineage, - ColumnLineageSource, - ModelColumnLineage, - PhysicalResource, -) -from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, - InferredNullability, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_AGGREGATE_KINDS, - POLYGLOT_CAST_KINDS, - POLYGLOT_KIND_ALIAS, - POLYGLOT_KIND_COLUMN, - POLYGLOT_KIND_SELECT, - POLYGLOT_KIND_TABLE, - POLYGLOT_KIND_UNION, - POLYGLOT_SET_OPERATION_KINDS, -) -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.diagnostics.main.log_debug_event import log_debug_event - -_DEBUG_LOGGER: logging.Logger = logging.getLogger("sqlbuild.lineage") - - -def _build_polyglot_fast_model_column_lineage( - *, - model: CompiledModel, - schema: dict[str, dict[str, str]], - dialect: str | None, -) -> ModelColumnLineage | None: - if model.fast_lineage_columns is not None: - lineages: list[ColumnLineage] = [] - for fact in model.fast_lineage_columns: - upstream_columns: list[ColumnLineageSource] = [] - for source in fact.upstream_columns: - upstream_columns.append( - ColumnLineageSource( - resource_type=source.resource_type, - resource_name=source.resource_name, - column_name=source.column_name, - ) - ) - lineages.append( - ColumnLineage( - output_column=fact.output_column, - transform_kind=fact.transform_kind, - expression_sql=None, - upstream_columns=tuple(upstream_columns), - nullability=InferredNullability.UNKNOWN, - confidence=fact.confidence, - ) - ) - if model.fast_lineage_has_star and not model.fast_lineage_star_resolved: - normalized_sql: str - physical_resources: tuple[PhysicalResource, ...] - normalized_sql, physical_resources = _normalize_sqlbuild_refs(model.query_sql) - del normalized_sql - lineages.extend( - _build_star_lineage( - model=model, - schema=schema, - physical_resources=physical_resources, - existing_columns={lineage.output_column for lineage in lineages}, - ) - ) - return ModelColumnLineage( - model_name=model.name, - columns=tuple(lineages), - has_star=model.fast_lineage_has_star, - ) - - polyglot_module: Any = import_polyglot_sql() - normalized_sql: str - physical_resources: tuple[PhysicalResource, ...] - normalized_sql, physical_resources = _normalize_sqlbuild_refs(model.query_sql) - try: - parsed: Any = polyglot_module.parse_one(normalized_sql, dialect=dialect or "generic") - except polyglot_module.PolyglotError as error: - log_debug_event( - logger=_DEBUG_LOGGER, - message="fast column lineage parse failed; falling back", - sqlbuild_model=model.name, - sqlbuild_error=str(error), - ) - return None - parsed_kind: str = str(getattr(parsed, "kind", "")) - if parsed_kind == POLYGLOT_KIND_UNION: - return _build_polyglot_union_model_column_lineage( - model=model, - parsed=parsed, - schema=schema, - physical_resources=physical_resources, - ) - if parsed_kind != POLYGLOT_KIND_SELECT: - return None - - alias_map: dict[str, PhysicalResource] = _polyglot_table_alias_map( - parsed=parsed, - physical_resources=physical_resources, - ) - unqualified_resource: PhysicalResource | None = _single_alias_resource(alias_map) - lineages: list[ColumnLineage] = [] - has_star: bool = False - projections: tuple[Any, ...] = tuple(getattr(parsed, "expressions", ())) - inferred_names: tuple[str, ...] = tuple(column.name for column in model.inferred_columns or ()) - for index, projection in enumerate(projections): - if bool(getattr(projection, "is_star", False)): - has_star = True - continue - inner: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == POLYGLOT_KIND_ALIAS - else projection - ) - if bool(getattr(inner, "is_star", False)): - has_star = True - continue - output_column: str | None = _polyglot_projection_output_name( - projection=projection, - index=index, - inferred_names=inferred_names, - ) - if output_column is None: - continue - upstream_columns, confidence = _polyglot_projection_upstream_columns( - projection=projection, - alias_map=alias_map, - unqualified_resource=unqualified_resource, - ) - transform_kind: ColumnTransformKind = _polyglot_classify_transform( - expression=inner, - upstream_columns=upstream_columns, - ) - lineages.append( - ColumnLineage( - output_column=output_column, - transform_kind=transform_kind, - expression_sql=None, - upstream_columns=upstream_columns, - nullability=InferredNullability.UNKNOWN, - confidence=confidence - if upstream_columns or transform_kind == ColumnTransformKind.CONSTANT - else ColumnLineageConfidence.UNKNOWN, - ) - ) - - if has_star: - lineages.extend( - _build_star_lineage( - model=model, - schema=schema, - physical_resources=physical_resources, - existing_columns={lineage.output_column for lineage in lineages}, - ) - ) - return ModelColumnLineage(model_name=model.name, columns=tuple(lineages), has_star=has_star) - - -def _build_polyglot_union_model_column_lineage( - *, - model: CompiledModel, - parsed: Any, - schema: dict[str, dict[str, str]], - physical_resources: tuple[PhysicalResource, ...], -) -> ModelColumnLineage | None: - selects: tuple[Any, ...] = _polyglot_set_expression_selects(parsed) - if not selects: - return None - inferred_names: tuple[str, ...] = tuple(column.name for column in model.inferred_columns or ()) - max_projection_count: int = max( - (len(tuple(getattr(select, "expressions", ()) or ())) for select in selects), - default=0, - ) - lineages: list[ColumnLineage] = [] - has_star: bool = False - for index in range(max_projection_count): - output_column: str | None = inferred_names[index] if index < len(inferred_names) else None - upstream_columns: list[ColumnLineageSource] = [] - seen: set[tuple[CompiledResourceType, str, str]] = set() - confidence: ColumnLineageConfidence = ColumnLineageConfidence.HIGH - transform_kind: ColumnTransformKind = ColumnTransformKind.DIRECT - for select in selects: - projections: tuple[Any, ...] = tuple(getattr(select, "expressions", ()) or ()) - if index >= len(projections): - continue - projection: Any = projections[index] - if bool(getattr(projection, "is_star", False)): - has_star = True - continue - inner: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == POLYGLOT_KIND_ALIAS - else projection - ) - if bool(getattr(inner, "is_star", False)): - has_star = True - continue - if output_column is None: - output_column = _polyglot_projection_output_name( - projection=projection, - index=index, - inferred_names=inferred_names, - ) - alias_map: dict[str, PhysicalResource] = _polyglot_table_alias_map( - parsed=select, - physical_resources=physical_resources, - ) - branch_upstream, branch_confidence = _polyglot_projection_upstream_columns( - projection=projection, - alias_map=alias_map, - unqualified_resource=_single_alias_resource(alias_map), - ) - if branch_confidence == ColumnLineageConfidence.UNKNOWN: - confidence = ColumnLineageConfidence.UNKNOWN - elif ( - branch_confidence == ColumnLineageConfidence.MEDIUM - and confidence == ColumnLineageConfidence.HIGH - ): - confidence = ColumnLineageConfidence.MEDIUM - branch_transform: ColumnTransformKind = _polyglot_classify_transform( - expression=inner, - upstream_columns=branch_upstream, - ) - if branch_transform != ColumnTransformKind.DIRECT: - transform_kind = branch_transform - for source in branch_upstream: - key: tuple[CompiledResourceType, str, str] = ( - CompiledResourceType(source.resource_type), - source.resource_name, - source.column_name, - ) - if key in seen: - continue - seen.add(key) - upstream_columns.append(source) - if output_column is None: - continue - if not upstream_columns and transform_kind != ColumnTransformKind.CONSTANT: - confidence = ColumnLineageConfidence.UNKNOWN - lineages.append( - ColumnLineage( - output_column=output_column, - transform_kind=transform_kind, - expression_sql=None, - upstream_columns=tuple(upstream_columns), - nullability=InferredNullability.UNKNOWN, - confidence=confidence, - ) - ) - if has_star: - lineages.extend( - _build_star_lineage( - model=model, - schema=schema, - physical_resources=physical_resources, - existing_columns={lineage.output_column for lineage in lineages}, - ) - ) - return ModelColumnLineage(model_name=model.name, columns=tuple(lineages), has_star=has_star) - - -def _polyglot_set_expression_selects(expression: Any) -> tuple[Any, ...]: - kind: str = str(getattr(expression, "kind", "")) - if kind == POLYGLOT_KIND_SELECT: - return (expression,) - if kind not in POLYGLOT_SET_OPERATION_KINDS: - return () - args: object = getattr(expression, "args", {}) - if not isinstance(args, dict): - return () - selects: list[Any] = [] - for child_name in ("left", "right"): - child: object = args.get(child_name) - if child is not None: - selects.extend(_polyglot_set_expression_selects(child)) - return tuple(selects) - - -def _polyglot_table_alias_map( - *, - parsed: Any, - physical_resources: tuple[PhysicalResource, ...], -) -> dict[str, PhysicalResource]: - physical_resource_by_name: dict[str, PhysicalResource] = { - resource.physical_name: resource for resource in physical_resources - } - physical_resource_by_name.update( - {resource.resource_name: resource for resource in physical_resources} - ) - alias_map: dict[str, PhysicalResource] = {} - tables: tuple[Any, ...] = tuple(parsed.find_all(POLYGLOT_KIND_TABLE)) - for table in tables: - table_name: str = str(getattr(table, "name", "") or "") - resource: PhysicalResource | None = physical_resource_by_name.get(table_name) - if resource is None: - continue - alias_map[table_name] = resource - alias_or_name: str = str(getattr(table, "alias_or_name", "") or "") - if alias_or_name: - alias_map[alias_or_name] = resource - return alias_map - - -def _polyglot_projection_output_name( - *, projection: Any, index: int, inferred_names: tuple[str, ...] -) -> str | None: - raw_name: str = str(getattr(projection, "output_name", "") or "") - if raw_name and raw_name != STAR_COLUMN_NAME: - return raw_name - raw_name = str(getattr(projection, "alias_or_name", "") or "") - if raw_name and raw_name != STAR_COLUMN_NAME: - return raw_name - if index < len(inferred_names): - return inferred_names[index] - return None - - -def _polyglot_projection_upstream_columns( - *, - projection: Any, - alias_map: dict[str, PhysicalResource], - unqualified_resource: PhysicalResource | None, -) -> tuple[tuple[ColumnLineageSource, ...], ColumnLineageConfidence]: - columns: list[ColumnLineageSource] = [] - seen: set[tuple[CompiledResourceType, str, str]] = set() - confidence: ColumnLineageConfidence = ColumnLineageConfidence.HIGH - column_refs: tuple[tuple[str, str], ...] = _polyglot_column_refs_in_expression(projection) - for column_name, table_name in column_refs: - if not column_name: - continue - resource: PhysicalResource | None = None - if table_name: - resource = alias_map.get(table_name) - elif unqualified_resource is not None: - resource = unqualified_resource - confidence = ColumnLineageConfidence.MEDIUM - else: - confidence = ColumnLineageConfidence.UNKNOWN - if resource is None: - continue - key: tuple[CompiledResourceType, str, str] = ( - resource.resource_type, - resource.resource_name, - column_name, - ) - if key in seen: - continue - seen.add(key) - columns.append( - ColumnLineageSource( - resource_type=resource.resource_type, - resource_name=resource.resource_name, - column_name=column_name, - ) - ) - return tuple(columns), confidence - - -def _single_alias_resource(alias_map: dict[str, PhysicalResource]) -> PhysicalResource | None: - resource: PhysicalResource | None = None - for candidate in alias_map.values(): - if resource is None: - resource = candidate - continue - if candidate.resource_name != resource.resource_name: - return None - return resource - - -def _polyglot_column_refs_in_expression(expression: Any) -> tuple[tuple[str, str], ...]: - if str(getattr(expression, "kind", "")) == POLYGLOT_KIND_COLUMN: - return ( - (str(getattr(expression, "name", "") or ""), _polyglot_column_table_name(expression)), - ) - payload: object = expression.to_dict() - refs: list[tuple[str, str]] = [] - - def visit(*, node: object, collected_refs: list[tuple[str, str]]) -> list[tuple[str, str]]: - if isinstance(node, dict): - node_dict: dict[str, object] = cast(dict[str, object], node) - column_payload: object = node_dict.get("column") - if isinstance(column_payload, dict): - column_dict: dict[str, object] = cast(dict[str, object], column_payload) - column_name: str = _polyglot_name_payload_value(column_dict.get("name")) - table_payload: object = column_dict.get("table") - table_name: str = "" - if isinstance(table_payload, dict): - table_dict: dict[str, object] = cast(dict[str, object], table_payload) - table_name = _polyglot_name_payload_value(table_dict.get("name")) - return [*collected_refs, (column_name, table_name)] - for value in node_dict.values(): - collected_refs = visit(node=value, collected_refs=collected_refs) - elif isinstance(node, list): - for value in node: - collected_refs = visit(node=value, collected_refs=collected_refs) - return collected_refs - - refs = visit(node=payload, collected_refs=refs) - return tuple(refs) - - -def _polyglot_name_payload_value(payload: object) -> str: - if isinstance(payload, str): - return payload - if isinstance(payload, dict): - payload_dict: dict[str, object] = cast(dict[str, object], payload) - name: object = payload_dict.get("name") - if isinstance(name, str): - return name - return "" - - -def _polyglot_column_table_name(column: Any) -> str: - payload: object = column.to_dict().get("column", {}) - if not isinstance(payload, dict): - return "" - table_payload: object = payload.get("table") - if not isinstance(table_payload, dict): - return "" - raw_name: object = table_payload.get("name") - return raw_name if isinstance(raw_name, str) else "" - - -def _polyglot_classify_transform( - *, - expression: Any, - upstream_columns: tuple[ColumnLineageSource, ...], -) -> ColumnTransformKind: - kind: str = str(getattr(expression, "kind", "")) - if bool(getattr(expression, "is_star", False)): - return ColumnTransformKind.STAR - if kind in POLYGLOT_CAST_KINDS: - return ColumnTransformKind.CAST - if _polyglot_has_aggregation(expression): - return ColumnTransformKind.AGGREGATION - if not upstream_columns: - return ColumnTransformKind.CONSTANT - if kind == POLYGLOT_KIND_COLUMN and len(upstream_columns) == 1: - return ColumnTransformKind.DIRECT - return ColumnTransformKind.EXPRESSION - - -def _polyglot_has_aggregation(expression: Any) -> bool: - nodes: tuple[Any, ...] = tuple(expression.walk()) - return any(str(getattr(node, "kind", "")) in POLYGLOT_AGGREGATE_KINDS for node in nodes) diff --git a/src/sqlbuild/compiler/lineage/_helpers/native_deferrals.py b/src/sqlbuild/compiler/lineage/_helpers/native_deferrals.py deleted file mode 100644 index 8d7e3cd387..0000000000 --- a/src/sqlbuild/compiler/lineage/_helpers/native_deferrals.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Debug-only record of fast lineage models the native engine hands back to Python.""" - -from __future__ import annotations - -import json -import os -from pathlib import Path - -from sqlbuild.compiler.lineage.constants import ( - ANALYSIS_DEFERRAL_RECORD_PREFIX, - NATIVE_LINEAGE_DEFERRAL_SITE, -) -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR - - -def record_lineage_deferral(*, kind: str) -> None: - """Append one `{"kind", "site"}` line when the analysis record directory is set.""" - - directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) - if not directory: - return - target: Path = Path(directory) - target.mkdir(parents=True, exist_ok=True) - record: str = json.dumps({"kind": kind, "site": NATIVE_LINEAGE_DEFERRAL_SITE}) - with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: - _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py index 746c185c81..c59abdc798 100644 --- a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py +++ b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py @@ -14,14 +14,8 @@ CompiledProject, ) from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.lineage._helpers.columns import _build_schema_mapping -from sqlbuild.compiler.lineage._helpers.fast_columns import ( - _build_polyglot_fast_model_column_lineage, -) -from sqlbuild.compiler.lineage._helpers.native_deferrals import record_lineage_deferral from sqlbuild.compiler.lineage.constants import ( NATIVE_LINEAGE_BUILT, - NATIVE_LINEAGE_DEFERRED, NATIVE_LINEAGE_UNKNOWN_DIALECT, NATIVE_LINEAGE_UNPARSED, ) @@ -71,7 +65,6 @@ def build_native_fast_project_column_lineage( else [] ) outcome_index: int = 0 - python_schema: dict[str, dict[str, str]] | None = None model_results: dict[str, ModelColumnLineage] = {} compact_models: dict[str, tuple[Sequence[CompiledLineageColumnFact], bool]] = {} model_order: list[str] = [] @@ -103,13 +96,6 @@ def build_native_fast_project_column_lineage( ) elif status == NATIVE_LINEAGE_UNKNOWN_DIALECT: raise ValueError(f"Unknown dialect: {detail}") - elif status == NATIVE_LINEAGE_DEFERRED: - record_lineage_deferral(kind=str(detail)) - if python_schema is None: - python_schema = _build_schema_mapping(project) - result = _build_polyglot_fast_model_column_lineage( - model=model, schema=python_schema, dialect=dialect - ) if result is None: continue model_results[model.name] = result diff --git a/src/sqlbuild/compiler/lineage/constants.py b/src/sqlbuild/compiler/lineage/constants.py index 528a7bd66a..088054641c 100644 --- a/src/sqlbuild/compiler/lineage/constants.py +++ b/src/sqlbuild/compiler/lineage/constants.py @@ -5,7 +5,5 @@ INDEXED_EDGE_RECORD_LENGTH: int = 6 NATIVE_LINEAGE_BUILT: str = "built" NATIVE_LINEAGE_UNPARSED: str = "unparsed" -NATIVE_LINEAGE_DEFERRED: str = "deferred" NATIVE_LINEAGE_UNKNOWN_DIALECT: str = "unknown_dialect" -NATIVE_LINEAGE_DEFERRAL_SITE: str = "fast_columns.py" ANALYSIS_DEFERRAL_RECORD_PREFIX: str = "analysis-deferrals-" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py index e8d1fe7758..fab6ff3cec 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py @@ -3,31 +3,24 @@ import json import re import subprocess -from collections import Counter from itertools import product from pathlib import Path from typing import NamedTuple, cast from sqlbuild.cli.compile_reuse.constants import REUSE_DISABLE_ENV_VAR -from sqlbuild.compiler.lineage.constants import NATIVE_LINEAGE_DEFERRAL_SITE from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import run_installed_sqb _TIMING: re.Pattern[str] = re.compile(r" \(\d+\.\d+s\)") _DIRECTIONS: tuple[str, ...] = ("upstream", "downstream") -FALLBACK_SITE: str = ( - "compiler/lineage/_helpers/fast_columns.py:_build_polyglot_fast_model_column_lineage" -) class EngineLineageRun(NamedTuple): - """One engine's compile report, lineage traces, wheel fallback parses and lineage deferrals.""" + """One engine's compile report and lineage traces.""" compile_report: str compile_returncode: int traces: list[tuple[int, str, str]] - fallback_parses: int - lineage_deferrals: int def engine_lineage_run( @@ -75,11 +68,6 @@ def engine_lineage_run( compile_report=json.dumps(report, indent=2), compile_returncode=compiled.returncode, traces=traces, - fallback_parses=_site_calls(record_dir)[FALLBACK_SITE], - lineage_deferrals=sum( - path.read_text("utf-8").count(f'"site": "{NATIVE_LINEAGE_DEFERRAL_SITE}"') - for path in record_dir.glob("analysis-deferrals-*.jsonl") - ), ) @@ -91,11 +79,3 @@ def model_lineage_summaries(compile_report: str) -> dict[str, object]: list[dict[str, object]], cast(dict[str, object], payload["resources"])["models"] ) return {str(model["name"]): model["lineage"] for model in models} - - -def _site_calls(record_dir: Path) -> Counter[str]: - calls: Counter[str] = Counter() - for path in record_dir.glob("polyglot-sites-*.json"): - for site, _, count in json.loads(path.read_text("utf-8"))["calls"]: - calls[site] += count - return calls diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py index ae581195b3..f1a07bf755 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py @@ -135,7 +135,6 @@ def test_given_project_when_compiling_with_each_engine_then_fast_lineage_output_ test_case.expected_minimum_traced_edges ) assert runs["native-preview"] == native - assert native.fallback_parses == 0 if __name__ == "__main__": diff --git a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py index 5089f90443..5abec348b2 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py @@ -35,3 +35,13 @@ class UnparsedLineageTestCase: query_sql: str expected_status: str expected_messages: tuple[str, ...] + + +@dataclass(frozen=True) +class ParserPanicLineageTestCase: + """A model whose SQL panics the native parser, failing lineage as an internal error.""" + + description: str + dialect: str + query_sql: str + expected_message: str diff --git a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py index 125401e8e5..2ab26f0327 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py @@ -10,6 +10,7 @@ import pytest +import sqlbuild._native as native_module from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.lineage.main._build_native_column_lineage import ( build_native_column_lineage, @@ -20,6 +21,7 @@ from tests.integration.src.sqlbuild.compiler.lineage._test_types import ( FormerlyDeferredLineageTestCase, GeneratedLineageParityTestCase, + ParserPanicLineageTestCase, UnparsedLineageTestCase, ) from tests.integration.src.sqlbuild.compiler.lineage.helpers import ( @@ -192,5 +194,41 @@ def test_given_formerly_deferred_models_when_building_fast_lineage_then_native_a assert deferral_records(record_dir) == [] +@pytest.mark.parametrize( + "test_case", + [ + ParserPanicLineageTestCase( + description="truncated T-SQL the parser panics on, which crashes the wheel's process", + dialect="tsql", + query_sql="SELECT IF(region > 1, re", + expected_message=( + "NativeCompilerError: native SQL compilation panicked " + "(native fast lineage of request model 0)" + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_parser_panic_when_building_fast_lineage_then_raises_native_compiler_error( + test_case: ParserPanicLineageTestCase, tmp_path: Path +) -> None: + project: CompiledProject = compiled_project( + project_dir=tmp_path / "project", + files=generated_lineage_files(rng=random.Random(11), model_count=_MODEL_COUNT), + ) + broken: CompiledProject = replace( + project, + models=( + replace(project.models[0], query_sql=test_case.query_sql, fast_lineage_columns=None), + *project.models[1:], + ), + ) + + with pytest.raises(native_module.NativeCompilerError) as raised: + _ = build_native_column_lineage(project=broken, dialect=test_case.dialect, model_names=None) + + assert str(raised.value) == test_case.expected_message + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From fb7f3625915663f72dc3210f20a17ae3b156c20b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:08:41 +0100 Subject: [PATCH 12/36] test(native): pin former Python-oracle cases to recorded expected outputs Native-vs-native cache comparisons prove cache consistency only, so the generated analysis, FAST lineage, type and contract tests now also match digests recorded once from the pre-port Python implementations. Restores the deleted shared-binding tests' explicit diagnostics and lineage expectations as native end-to-end tests, keeps postgres, motherduck and sqlserver alias routes covered at the compile boundary, drops tests and helpers that patched deleted Python seams, and brings the native stubs in line with the raising session API. --- src/sqlbuild/_native.pyi | 87 +- .../_helpers/model_analysis.py | 4 +- .../cli/commands/main/compile/helpers.py | 15 - .../compile/test_compile_incremental_edits.py | 7 - ...self_and_a_star_consumer_of_the_cycle.json | 134 + ...ullability_rules_called_back_natively.json | 626 + ...ntyped_inputs_cte_facts_and_contracts.json | 3458 +++ ...and_duckdb_adapter_rules_fast_lineage.json | 1250 + ...and_duckdb_adapter_rules_rich_lineage.json | 1250 + ...tions_untyped_inputs_contracts_pivots.json | 3458 +++ ...g_column_readers_and_a_unique_summary.json | 148 + ...ped_nullability_and_dynamic_contracts.json | 722 + ...erations_quoting_and_opted_out_models.json | 5570 ++++ ...rized_nested_padded_and_invalid_types.json | 25502 ++++++++++++++++ .../adapter/type_system/_test_types.py | 3 +- .../test_native_type_normalization.py | 24 + .../compiler/analysis_session/_test_types.py | 10 + .../compiler/analysis_session/helpers.py | 19 +- .../test_native_model_analysis.py | 23 + .../test_shared_query_outputs.py | 225 + .../sqlbuild/compiler/contracts/helpers.py | 20 + .../contracts/test_native_model_contracts.py | 32 +- .../src/sqlbuild/compiler/golden_views.py | 120 + .../lineage/test_native_fast_lineage.py | 27 +- .../compiler/pipeline/test_binding_waves.py | 15 +- .../compiler/pipeline/test_review_bindings.py | 22 +- 26 files changed, 42656 insertions(+), 115 deletions(-) create mode 100644 tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json create mode 100644 tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json create mode 100644 tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json create mode 100644 tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json create mode 100644 tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json create mode 100644 tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json create mode 100644 tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json create mode 100644 tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json create mode 100644 tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json create mode 100644 tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json create mode 100644 tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py create mode 100644 tests/integration/src/sqlbuild/compiler/golden_views.py diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 94da5fc929..5e90fd808b 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -12,6 +12,9 @@ from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestAssemblyRow, Nati BUILD_IDENTITY: str +class NativeCompilerError(RuntimeError): + """An internal native compiler failure: a panic or a broken native invariant.""" + class LintPreparationRequest(TypedDict): expanded: str before_expansion: str @@ -381,79 +384,43 @@ class NativeModelAnalysisSession: def fact_models(self) -> list[str]: ... def run( self, - ) -> ( - tuple[ - list[tuple[str, list[tuple[str, str]]]], - list[ - tuple[ - str, - int, - str | None, - list[tuple[str, list[tuple[str, str]]]], - list[_AnalysisDiagnosticRow], - list[tuple[str, int, int, list[tuple[str, str, str]]]] | None, - ] - ], - list[str], - ] - | None - ): ... - def provide( + ) -> tuple[list[tuple[str, list[tuple[str, str]]]], list[str]]: ... + def finish( self, - results: list[ + ) -> tuple[ + list[ tuple[ - int, bool, list[tuple[str, str | None, str]] | None, + str, + list[tuple[str, int, int, list[tuple[str, str, str]]]], bool, bool, list[_AnalysisDiagnosticRow], bool, + str, ] ], - /, - ) -> bool: ... - def finish( - self, - ) -> ( - tuple[ - list[ + list[tuple[str, list[tuple[str, str]]]], + list[str], + list[ + tuple[ + str, tuple[ bool, - list[tuple[str, str | None, str]] | None, - str, - list[tuple[str, int, int, list[tuple[str, str, str]]]], - bool, - bool, - list[_AnalysisDiagnosticRow], + list[tuple[str, str | None, str]], + list[tuple[str, str | None]], + list[str], + str | None, bool, - str, ] - ], - list[tuple[str, list[tuple[str, str]]]], - list[str], - list[ - tuple[ - str, - tuple[ - bool, - list[tuple[str, str | None, str]], - list[tuple[str, str | None]], - list[str], - str | None, - bool, - ] - | None, - ] - ], - ] - | None - ): ... + | None, + ] + ], + ]: ... def prove_dynamic_contracts( self, models: list[tuple[str, list[tuple[str, str, str, str, str, str | None]]]], / - ) -> list[_PivotContractRow] | None: ... - @property - def failure(self) -> str | None: ... + ) -> list[_PivotContractRow]: ... @property def sharing(self) -> tuple[int, int]: ... @property @@ -463,8 +430,8 @@ def start_model_analysis_session( catalog: object, request: tuple[object, ...], cache: tuple[str, str] | None = None, - adapter_rules: tuple[dict[str, Callable[..., object]], type] | None = None, -) -> NativeModelAnalysisSession | None: ... + adapter_rules: tuple[Mapping[str, Callable[..., object]], type] | None = None, +) -> NativeModelAnalysisSession: ... def prove_dynamic_column_contracts( request: tuple[ str, @@ -475,7 +442,7 @@ def prove_dynamic_column_contracts( list[tuple[str, list[tuple[str, str, str, str, str, str | None]]]], ], /, -) -> list[_PivotContractRow] | None: ... +) -> list[_PivotContractRow]: ... def infer_expression_source_shapes( catalog: object, request: tuple[str, bool, list[tuple[str, str]], list[str]], / ) -> tuple[list[tuple[bool, list[tuple[str, str]] | None]], str | None]: ... diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py index a081c392fa..487b40c615 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any +from typing import Any, cast import sqlbuild._native as _native from sqlbuild.adapter.contract.types import FunctionNullabilityRule @@ -32,7 +32,7 @@ def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModel if not request.model_inputs: return NativeModelAnalyses(analyses={}, session=None) - catalog: Any = request.inference_profile.binding_catalog + catalog: Any = cast(Any, request.inference_profile.binding_catalog) python: PythonModelAnalysis = PythonModelAnalysis( model_inputs=request.model_inputs, inference_profile=request.inference_profile, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py index e8efafe438..5cc72053ff 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py @@ -38,7 +38,6 @@ import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.diagnostics.recovery as diagnostic_recovery import sqlbuild.compiler.compile._helpers.macro_bridge.call_store as call_store_module -import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_assembly import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stages import sqlbuild.compiler.compile._helpers.native_stages.sql_tests as native_sql_test_stage import sqlbuild.compiler.contracts.main.promotion_conflicts as promotion_conflicts @@ -4286,20 +4285,6 @@ def ignore_project_changes(monkeypatch: pytest.MonkeyPatch) -> None: ) -def ignore_query_in_analysis_key(monkeypatch: pytest.MonkeyPatch) -> None: - """Key Python's cached model analyses without their query, so an edited query reads a stale - analysis; native analysis defers to that Python analysis, since it keys its own cache.""" - - monkeypatch.setattr(native_assembly, "analyze_native_model_sql", lambda **_: None) - analysis_key: Callable[..., str] = project_assembly.model_analysis_cache_key - - def query_blind_key(**kwargs: Any) -> str: - kwargs["query_sql"] = "" - return analysis_key(**kwargs) - - monkeypatch.setattr(project_assembly, "model_analysis_cache_key", query_blind_key) - - EXTERNAL_FLAVOR_MODULE: str = "extflavor" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py index a02459ad51..c3aa8d64a5 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py @@ -47,7 +47,6 @@ fact_quantity_type_declared, fact_quantity_type_removed, ignore_project_changes, - ignore_query_in_analysis_key, ignore_test_text_in_scan_key, in_process_reuse_run, keep_invalidation, @@ -372,12 +371,6 @@ def test_given_random_edit_chain_when_compiling_with_caches_then_each_step_match sabotage=keep_invalidation, expected_matches_uncached=True, ), - BrokenEditInvalidationTestCase( - description="stale_python_analysis_despite_query_edit", - edit=fact_error_introduced, - sabotage=ignore_query_in_analysis_key, - expected_matches_uncached=False, - ), BrokenEditInvalidationTestCase( description="intact_invalidation_after_test_edit", edit=stg_orders_test_edit, diff --git a/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json b/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json new file mode 100644 index 0000000000..84e00f8f26 --- /dev/null +++ b/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json @@ -0,0 +1,134 @@ +[ +[ +"shapes", +"75c111c4221d031b0df0f644" +], +[ +"models", +{ +"order_audit": "102a144114b4a8a3e464d565", +"order_summary": "ca21b0318683cc6b7506af3d", +"orders": "412acfefd9b029b3a46752c7", +"returns": "0d43c64379f12801c6a42e57" +} +], +[ +"catalog", +"2b8932e9fd162ab0b58d4e8f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"75c111c4221d031b0df0f644" +], +[ +"models", +{ +"order_audit": "102a144114b4a8a3e464d565", +"order_summary": "ca21b0318683cc6b7506af3d", +"orders": "6a1622e4397f1eef49fe9b3b", +"returns": "0d43c64379f12801c6a42e57" +} +], +[ +"catalog", +"2b8932e9fd162ab0b58d4e8f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"75c111c4221d031b0df0f644" +], +[ +"models", +{ +"order_audit": "102a144114b4a8a3e464d565", +"order_summary": "ca21b0318683cc6b7506af3d", +"orders": "412acfefd9b029b3a46752c7", +"returns": "0d43c64379f12801c6a42e57" +} +], +[ +"catalog", +"2b8932e9fd162ab0b58d4e8f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"75c111c4221d031b0df0f644" +], +[ +"models", +{ +"order_audit": "102a144114b4a8a3e464d565", +"order_summary": "ca21b0318683cc6b7506af3d", +"orders": "6a1622e4397f1eef49fe9b3b", +"returns": "50c61ef317c36e693b607753" +} +], +[ +"catalog", +"2b8932e9fd162ab0b58d4e8f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json b/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json new file mode 100644 index 0000000000..489f0beb0c --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json @@ -0,0 +1,626 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "df6e4a58863118447ccd6a56", +"orders_model_10": "bcbd615c256c35337aed2db2", +"orders_model_11": "47923badaec7c00915e5b4bb", +"orders_model_12": "1cc095a7cdb49d517637e721", +"orders_model_13": "800b7f2cfae2d608b60c7ece", +"orders_model_14": "22c002e97f6de1fe4b9cdccd", +"orders_model_15": "eebd1c2c0322fa13fdc75504", +"orders_model_16": "2717ab2a04d761545073308b", +"orders_model_17": "8255e9b0e9b9fb715e5d1f58", +"orders_model_18": "28541fce67c204773859236c", +"orders_model_19": "9f23b26f2c338b8fc3e3e838", +"orders_model_2": "5a185b79563abbd167d23c9e", +"orders_model_20": "d594752e20c6f55a9ca9a40e", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "50215bcfc3981855df918ff1", +"orders_model_23": "2c1c11368b18534a2e433f73", +"orders_model_3": "f530981ae0183c1746020b51", +"orders_model_4": "e29709e8e7acb44cc782304f", +"orders_model_5": "0c6b7057337af7825d4f5905", +"orders_model_6": "4308568a0bb439fb417623b4", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "be94f9981f346459dd08eb00", +"orders_model_9": "7a96226f82f52625a17f825f", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "18ecbe5e93fe3ced5cbfd24a", +"pivot_1": "8ffb88803fba96f5e1a0cc14", +"pivot_2": "af151ba34f16c141fdee1ac3", +"pivot_3": "8d7a74ec77a418a6a2641b84", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"d360424e65e4f8f287f9a397" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "dc7f3dec778fd7cc3b47de47", +"orders_model_1": "372f6fed52ac7b76d5703db0", +"orders_model_10": "e0d9b7a41394deecb38e3086", +"orders_model_11": "a74efc4485502093e7925022", +"orders_model_12": "159cce49ef0548e95a5b46fe", +"orders_model_13": "03539cfdeb6da7bf1e9fe042", +"orders_model_14": "3fdd19b40a943161f6c6cc9e", +"orders_model_15": "ca04925bf918b53768e0398d", +"orders_model_16": "e29709e8e7acb44cc782304f", +"orders_model_17": "b98505b8785820593e460502", +"orders_model_18": "86967e1e1505ff93a0965cef", +"orders_model_19": "966cc0c652215c4b0d249ec4", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "84ef045e4fbe323e09d9d96b", +"orders_model_21": "ab446571e5d18dc83a4b3cf5", +"orders_model_22": "f1de8641e65deae07a766a44", +"orders_model_23": "8c59f6a9f31545711675187a", +"orders_model_3": "000b4e41decb10da91684d6e", +"orders_model_4": "bf62990d9480cf71956fd3c9", +"orders_model_5": "0a4ba3dd4c01cce9c3179015", +"orders_model_6": "36c156c5a6601badf8124464", +"orders_model_7": "2cc4699fcfbfc50f29d34e4d", +"orders_model_8": "ebd5177365eb50337f61b39a", +"orders_model_9": "a0b1dba8f656eaa0bfb2c9dd", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "af151ba34f16c141fdee1ac3", +"pivot_1": "db45532b4aa0746bb6155579", +"pivot_2": "60093df965b22d2f7c809ce4", +"pivot_3": "335dfa47b776524b62ee26e6", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"05058c2d6069f67034746afd" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "172c86dd8351e7740e167082", +"orders_model_10": "1348402b074ad555891e4bef", +"orders_model_11": "e0a1bcae33bbfb164c9468f6", +"orders_model_12": "0fbd9a675ff8d8560b0540a4", +"orders_model_13": "c414c20d1ede816d6b319333", +"orders_model_14": "d1550c8ea359faa939c59092", +"orders_model_15": "e0a6f9cf7645c417bdbdb2f3", +"orders_model_16": "6a3eeb4e49931817e15eec3a", +"orders_model_17": "d2f93646b187586cdc2ad5b6", +"orders_model_18": "252b1614ef32811f90405d55", +"orders_model_19": "20835381aab849fd35068525", +"orders_model_2": "541aad8bbf10a165003da4fa", +"orders_model_20": "9bb3b6be8506e6c3e809177f", +"orders_model_21": "c87751fbd20922b30ba3d847", +"orders_model_22": "264e63bb81a8673659bbb9db", +"orders_model_23": "ed8580ec8dc574a2041c68ee", +"orders_model_3": "4b659372639f55ac9ed99d36", +"orders_model_4": "c1496cb11408177cb813ba21", +"orders_model_5": "2d52cab3dcd8a4c198f59410", +"orders_model_6": "5ccadf27b4bf1a37e0f30682", +"orders_model_7": "16fc9c0348ca5eadfd147e95", +"orders_model_8": "1a1778883746da694b7922e3", +"orders_model_9": "7a96226f82f52625a17f825f", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "d7685388e49eb5b076c776ac", +"pivot_1": "60093df965b22d2f7c809ce4", +"pivot_2": "b4bb4c1927b422d4b8b9ebb9", +"pivot_3": "a3b575dd05964ac460755bbd", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"249032d056c80b1ec4ee3cea" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"15cac1a9d4a0c399d20eec68" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json new file mode 100644 index 0000000000..4ec7fcb8f0 --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json @@ -0,0 +1,3458 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "6f741725ae5781425358bc3b", +"orders_model_10": "c7e95ff7d90362e121ab238f", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "15537f706967682b808641f6", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "3dbaddb57a2ed5f8db44feda", +"orders_model_17": "ae6be31dbe60a1c0d17b30bc", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "4782036882204544bf9b8cc0", +"orders_model_2": "2e71b399a66b78700dd48317", +"orders_model_20": "00d5932c856360eb282ca536", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "b1c72c923aea0b59f8745317", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "f9183611419f32c78a12fbc4", +"orders_model_6": "7a0c234309501030bdc80d53", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "2e035b59097bd4547b20a7ca", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"7dae5e8f83fbe068e10fa8a1" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"ff4d4f462aae3b9e95d9a5ab" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"e718df74c851405aa9e05532" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "6f741725ae5781425358bc3b", +"orders_model_10": "c7e95ff7d90362e121ab238f", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "c7fa6f64bedfc1611fad26ba", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "6fe127037935cdca6ceb3f6e", +"orders_model_16": "3dbaddb57a2ed5f8db44feda", +"orders_model_17": "7cc87d8c32df82057dc6057d", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "27b686e0958c19d151cd6319", +"orders_model_20": "13b36d543da620ba9a151138", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "b1c72c923aea0b59f8745317", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "f4c3ccd3c76f843ac4bced8a", +"orders_model_6": "526c167cb476b3553de71042", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "55ea0527456ec442813d63d5", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"59adab814e12cc9aaaab308d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "541418fef292ad4a9c6f8dab", +"orders_model_1": "ff37aa9e31c0ddbc7f05cba2", +"orders_model_10": "315f07530cae631f3e373e9d", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "94f95487333b2561382c2981", +"orders_model_13": "f73e95ff8968d95ed11dc4f3", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "1a6c68f8be75e24bf3304cdb", +"orders_model_17": "ae6be31dbe60a1c0d17b30bc", +"orders_model_18": "79cac6cd1f2e605da47e460d", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "2e71b399a66b78700dd48317", +"orders_model_20": "13b36d543da620ba9a151138", +"orders_model_21": "c669dc21fed757d7809fc424", +"orders_model_22": "066b8f5bf2be63a888045fe7", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "b1c72c923aea0b59f8745317", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "f9183611419f32c78a12fbc4", +"orders_model_6": "f3d71446fe7a0576e2049a7a", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "55ea0527456ec442813d63d5", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"9f8ce1f318b9262fd1b830ff" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "9b0cc36921b5b8b8a8dea025", +"orders_model_10": "d4f810ff3ba14295385d6fb9", +"orders_model_11": "f7cdb34524d45acad3b71f7d", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "11f35c6f0b5918adfca2c40f", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "26974efa98703503eb817f97", +"orders_model_16": "c5a645b8cfb02ddc927fc297", +"orders_model_17": "669b30fefd09621ed0f2ad55", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_19": "5318b049f2f651a689fb5e87", +"orders_model_2": "361de61cfd22f09af946862f", +"orders_model_20": "407c766e409ebec28d0c232a", +"orders_model_21": "9c26876fe71c235386932362", +"orders_model_22": "86880e31d61c6c5522256c62", +"orders_model_23": "209048028aa76bf4c2bd28c6", +"orders_model_3": "3ed083bce693c01d151c8081", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "a89cb10ab6dfef246cc36b07", +"orders_model_6": "a903f36871fea6d8ac818922", +"orders_model_7": "a4c400c5fa2da4107098339e", +"orders_model_8": "5400ca61f1ed2f29d89e2d23", +"orders_model_9": "67dc0f1b5ce04c8989ad1aca", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"c44c8633473bde950e573766" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "73d5e99497d80b919e0d5491", +"orders_model_10": "31ba43d1ed6f0701481c543b", +"orders_model_11": "e68784174562f0ac8afbc614", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_13": "3676088fa7438b6033da7334", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_15": "31fb137256076ba0126683aa", +"orders_model_16": "3122402ab671f4438c8209d7", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "7ccdde571d7f688040f1f47c", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "b9162cfbc1ce5044c01cca42", +"orders_model_20": "1bb9c3971b4488e0b7023645", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "5f2c3523ba8300fb944cc577", +"orders_model_23": "b760f8a3ed2b65af8ff70abc", +"orders_model_3": "f0f64c1baf69c4d7f0c75612", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "72717ea55695667a7b7d8b39", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"c17fe05b252117c41defdc1e" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"24ec11d3b54173b8b7b769ff" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "73d5e99497d80b919e0d5491", +"orders_model_10": "31ba43d1ed6f0701481c543b", +"orders_model_11": "3efd8cbce60b5adde2346a56", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_13": "cc456ea2e83298c9cd8206fb", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_15": "31fb137256076ba0126683aa", +"orders_model_16": "a6740f24eddec216a1afc653", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "3a3bfc6019ae1cf18c714dd0", +"orders_model_20": "1bb9c3971b4488e0b7023645", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "5f2c3523ba8300fb944cc577", +"orders_model_23": "4afbe759a7d6ab796db5ab1c", +"orders_model_3": "f0f64c1baf69c4d7f0c75612", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "63ebe9cbc63523f4c4adddb2", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "e6d82b48a7a5593bd747ea69", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"71c8c4df1603f87442ce9793" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "541418fef292ad4a9c6f8dab", +"orders_model_1": "828d747058993aeefdbae18a", +"orders_model_10": "9aefea09a1d167dbf49a2c69", +"orders_model_11": "e68784174562f0ac8afbc614", +"orders_model_12": "2cbf8e290082ab49db794db4", +"orders_model_13": "3676088fa7438b6033da7334", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_15": "31fb137256076ba0126683aa", +"orders_model_16": "b67d418ac9424b16909d6a26", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "b9162cfbc1ce5044c01cca42", +"orders_model_20": "1bb9c3971b4488e0b7023645", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "0b4ee9c2c4584613df0c4f3e", +"orders_model_23": "b760f8a3ed2b65af8ff70abc", +"orders_model_3": "f0f64c1baf69c4d7f0c75612", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "72717ea55695667a7b7d8b39", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"5f5c09917fe031a630ac277b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "3e1948d706ff1cecf42f7863", +"orders_model_10": "476ca953b1cb041d05ab9663", +"orders_model_11": "78eeedc510efd8cc89dfea31", +"orders_model_12": "b06fcf5168d718f9042eb9f0", +"orders_model_13": "25952bed7c264fceaeec357c", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_15": "2a978f96fe42e545ae0e5f8e", +"orders_model_16": "b67d418ac9424b16909d6a26", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "8a322bf0beb93c0034bd28d5", +"orders_model_19": "a4c400c5fa2da4107098339e", +"orders_model_2": "2f584b5185d90ee4c66c8339", +"orders_model_20": "1bb9c3971b4488e0b7023645", +"orders_model_21": "67dc0f1b5ce04c8989ad1aca", +"orders_model_22": "c838c05a0db9b7bdb5a1e793", +"orders_model_23": "18c951cb594f929547ec6527", +"orders_model_3": "cdd3d071e7283000e9b176dd", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "2f2cbf190f07bea2687a7a1e", +"orders_model_6": "b8c7c62baf6528acb751f82c", +"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", +"orders_model_8": "63ebe9cbc63523f4c4adddb2", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "0b5af099963f99173031a52f", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"f48ca046110efa93dc8e7b54" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "dc7f3dec778fd7cc3b47de47", +"orders_model_1": "98e73c916923e6ad3dbb4fe8", +"orders_model_10": "bba235f3ba5a071d31c89cfc", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "6d1049bdcad88b007d5f111c", +"orders_model_15": "f31a90a0505705305536f618", +"orders_model_16": "bf62990d9480cf71956fd3c9", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "f165784d55122e5e5f859766", +"orders_model_20": "f2329324ed72baeb2911ce6a", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_5": "c21e33abd058c0db4ed1aa0a", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "9a483c2d5942e8857be41233", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"b2b2221f2da5d47b86b56575" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"ff4d4f462aae3b9e95d9a5ab" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "34986e60aa464378baaa7f25", +"orders_model_1": "98e73c916923e6ad3dbb4fe8", +"orders_model_10": "9949660e9a41b3a11b1d646a", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "f31a90a0505705305536f618", +"orders_model_16": "e931c288a0cc5a80e1d59ebe", +"orders_model_17": "4cb4a5fe4f98fcb58b9aa0a8", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "f2329324ed72baeb2911ce6a", +"orders_model_21": "af5f39f23b7c2b58d3f12930", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "7faf6388dfebc22483c72417", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "e0e0e8448cdcfb7bbf411186", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"c5e050848445dd9e188a6f93" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "c0a4a49f398790b6968ded9b", +"orders_model_1": "f8df97502ce8987ade2b8ae6", +"orders_model_10": "d9e87a16e347f6f0316452d6", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "541418fef292ad4a9c6f8dab", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "6d1049bdcad88b007d5f111c", +"orders_model_15": "f31a90a0505705305536f618", +"orders_model_16": "0eca13897e0abb4131837a39", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "fb0385ba59d9d254edc4f53d", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "f165784d55122e5e5f859766", +"orders_model_20": "f2329324ed72baeb2911ce6a", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "83e085c3265373904422aad9", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_5": "c21e33abd058c0db4ed1aa0a", +"orders_model_6": "c55e2f85ad9de69066fc965c", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "ec0818249ce5c1a3abdbba12", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"0675d0c35333ce73c1709f47" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "279aba0c75055164f61b65f8", +"orders_model_1": "45de3ae2081edb1155411120", +"orders_model_10": "ce0bc9f834e9623dddad0b3a", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_12": "dc4da2070db56afde4157773", +"orders_model_13": "87a319b5caf7cc32bc6aa50e", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "18cc8b92ab2adfb7e7e160a3", +"orders_model_16": "28610c39619a490a015886ec", +"orders_model_17": "664cb83e35a8ecea0758a450", +"orders_model_18": "8cf38d3ff37cd8143561920a", +"orders_model_19": "dcb9d89d4b588b53682fb0b4", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "f2329324ed72baeb2911ce6a", +"orders_model_21": "af51dc83785211c066cbb51f", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "857c9a58882bddd68cec3bd5", +"orders_model_3": "d43b073d40eb97e87277c137", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "0541825c1a3bbc51c23f3a53", +"orders_model_7": "526a9e7adadbf085a39764b5", +"orders_model_8": "fcc5ac474974dee716908c65", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "0b5af099963f99173031a52f", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"7f3cc4630798c6d611e9c144" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "60ab36ff1481d98f182141fc", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "dc7f3dec778fd7cc3b47de47", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "1a083ad888e035969bd5f5fe", +"orders_model_16": "061d35dfeab6243e15890c04", +"orders_model_17": "458d9725be39144ca5e0c33d", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "959e49dd41f7bcc7e9edde19", +"orders_model_20": "40a0a6e57912d17b00b26274", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "e1986e4dea1c1d381bf078ff", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "458d9725be39144ca5e0c33d", +"orders_model_6": "d236f592f3b7c9328bb402ea", +"orders_model_7": "ef0eb5ded0428f49ac6cb411", +"orders_model_8": "19280031a53ac7db42aa7136", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"a749305bcf58d93756b4c9de" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "60ab36ff1481d98f182141fc", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "34986e60aa464378baaa7f25", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "1a083ad888e035969bd5f5fe", +"orders_model_16": "061d35dfeab6243e15890c04", +"orders_model_17": "458d9725be39144ca5e0c33d", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "40a0a6e57912d17b00b26274", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "e1986e4dea1c1d381bf078ff", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "458d9725be39144ca5e0c33d", +"orders_model_6": "36c156c5a6601badf8124464", +"orders_model_7": "80a1f1b7b38c1afa802d7625", +"orders_model_8": "19280031a53ac7db42aa7136", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "e6d82b48a7a5593bd747ea69", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"f0d8ca9bf661a94ff2124cfb" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "541418fef292ad4a9c6f8dab", +"orders_model_1": "673e75c341c3f2437091de09", +"orders_model_10": "8314d3a80b3a31f5a7b3b87c", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "c0a4a49f398790b6968ded9b", +"orders_model_13": "bc666dca3ca9016f640dfe68", +"orders_model_14": "7c3ab71786810446d45e97a3", +"orders_model_15": "1a083ad888e035969bd5f5fe", +"orders_model_16": "39b572633eb377051c189c82", +"orders_model_17": "7a9fabf5b3d3ab9e1fc63a99", +"orders_model_18": "79cac6cd1f2e605da47e460d", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "959e49dd41f7bcc7e9edde19", +"orders_model_20": "40a0a6e57912d17b00b26274", +"orders_model_21": "59228560ec9692b57930d8b5", +"orders_model_22": "b8238ed1e26320d8f8912882", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "e1986e4dea1c1d381bf078ff", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "7a9fabf5b3d3ab9e1fc63a99", +"orders_model_6": "748592fef86e52c625afd1e0", +"orders_model_7": "ef0eb5ded0428f49ac6cb411", +"orders_model_8": "19280031a53ac7db42aa7136", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"988cc4307f8cdbfd7c4e95f9" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "b6345243b4bb0009ec2dfb5d", +"orders_model_10": "408cbd03f775698d2714eb62", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_12": "279aba0c75055164f61b65f8", +"orders_model_13": "87a1dbf609e37b67c90e35b2", +"orders_model_14": "544d358c786985653996181d", +"orders_model_15": "e643f7ef7c9f860822ad8ba7", +"orders_model_16": "2ee8d0a917d274c8d55f4370", +"orders_model_17": "bac0193c7d43a68153ce544e", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_19": "ab249088d32d341a013be687", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "40a0a6e57912d17b00b26274", +"orders_model_21": "176a688a47e60b55e1ce0673", +"orders_model_22": "c6255585e2f5156964d8f8f8", +"orders_model_23": "e0ec1521e19c47996421ec94", +"orders_model_3": "f22120166b4143e8fd193e3d", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "bac0193c7d43a68153ce544e", +"orders_model_6": "c2b91215ea727d9313a22740", +"orders_model_7": "afb5b3102eaac7fa202355c0", +"orders_model_8": "19280031a53ac7db42aa7136", +"orders_model_9": "283c165f2da9102ffec377c4", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "0b5af099963f99173031a52f", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"767dbad2dc0bb5d013f7a196" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "abadf69a0572399b6536a2c1", +"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_11": "be02726b764e7cc94f5df56c", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "278d4fd453aea2eca14894ea", +"orders_model_14": "0759e673a754006cbe1427de", +"orders_model_15": "5e266036ddde9cb398819134", +"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "dd2512e1e685426785444ae5", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "b1cffebb5797fe684c20e885", +"orders_model_20": "9b30de5669877213eebaf0e5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "91d92b539582dec7737d1704", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "e29709e8e7acb44cc782304f", +"orders_model_5": "afd6d68f0a0a6689404e0260", +"orders_model_6": "7ccdde571d7f688040f1f47c", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "2307138f76b5c3d80ec84a7a", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"893ba59566e53ebc6aaa2cdc" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "7f4d684fd731d8fd8e517a52", +"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_12": "a030043d592af380cf44cb9f", +"orders_model_13": "56d09679844a976541cce33c", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_15": "5e266036ddde9cb398819134", +"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "f73c7edc9f35cb7956a40b52", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "9b30de5669877213eebaf0e5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "e780cb2018f0f8f501e8d2fe", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "e03d256f9a62a63d98fad738", +"orders_model_4": "9a1f8dc30a3cfc10bba60a45", +"orders_model_5": "afd6d68f0a0a6689404e0260", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "e7532f3dad5c3e5750265e5b", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"5d66ad3e2dacd496eb2b4872" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "1ad8f3013a29b4f16f037b39", +"orders_model_10": "3aec7a059badfa0e968d5216", +"orders_model_11": "be02726b764e7cc94f5df56c", +"orders_model_12": "cb6d3e8487b533349a2bad04", +"orders_model_13": "d55e6b7bb6533cea41bcfeda", +"orders_model_14": "0759e673a754006cbe1427de", +"orders_model_15": "5e266036ddde9cb398819134", +"orders_model_16": "97e322d50d70c30c61fb7a15", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "326d8bbf3c85b38aafb08a98", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "b1cffebb5797fe684c20e885", +"orders_model_20": "9b30de5669877213eebaf0e5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "368c97dc5eea624b492ce042", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "3af96276ee92b913506618b2", +"orders_model_5": "ee556ea5f790f1d885cee84a", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "ed49fa288ea02d542c88ec94", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"bb15ca51be7f1375a5b43886" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "1070e59d687e0fed0e44b651", +"orders_model_10": "fa26ab8510354dd670525ac2", +"orders_model_11": "eb7d097be111399bcd2f940f", +"orders_model_12": "4054517533ad5199a1077065", +"orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", +"orders_model_14": "273c339ab90e9a64a7c8f8a5", +"orders_model_15": "90310178ea5674f6669a72f4", +"orders_model_16": "e018db5f6e3eaf8888bd8eff", +"orders_model_17": "fe03c6a89b870943b6dd5922", +"orders_model_18": "fd5138ac4af02cd168a423e6", +"orders_model_19": "e24f113150b49c7e955cf81b", +"orders_model_2": "daca571af4a4d1cb1afbce74", +"orders_model_20": "9b30de5669877213eebaf0e5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "56f27ec8513b46291154e8cd", +"orders_model_23": "e94cdbeab01617cd05a55f82", +"orders_model_3": "72f0c384f9c169084cf65392", +"orders_model_4": "39a96779a764101f7d1dbae2", +"orders_model_5": "7af136b7e522ccf14f427e1e", +"orders_model_6": "8a322bf0beb93c0034bd28d5", +"orders_model_7": "4983a5e9302c8b9e28e75202", +"orders_model_8": "e7532f3dad5c3e5750265e5b", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "0b5af099963f99173031a52f", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"165df7e540546e89f76b0819" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "c909142a40bfa94a0849e672", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "159cce49ef0548e95a5b46fe", +"orders_model_13": "ba7bafc812a816c0c0770d82", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "e29709e8e7acb44cc782304f", +"orders_model_17": "40ca546bd222f4bd18e62461", +"orders_model_18": "2bcb5417ea2e03971c367fbd", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "0c798fa3e6d807bf94e96eee", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "52f7f68c35fc1813f86aee3b", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_5": "efc04632c3bd90f5ca705b61", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "653dbee5db7422883147ecf8", +"orders_model_9": "e2d21c84b2b62dea98a1d185", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "af151ba34f16c141fdee1ac3", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"f877c81e569d46b2f31adb8a" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "c909142a40bfa94a0849e672", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "159cce49ef0548e95a5b46fe", +"orders_model_13": "b929a7472b014a9b5b757127", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "fcf8d3dfd18bb30aef04a7e0", +"orders_model_16": "9a1f8dc30a3cfc10bba60a45", +"orders_model_17": "40ca546bd222f4bd18e62461", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "0c798fa3e6d807bf94e96eee", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "52f7f68c35fc1813f86aee3b", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_5": "efc04632c3bd90f5ca705b61", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "588e1b85c85669d7d2f2874a", +"orders_model_9": "e2d21c84b2b62dea98a1d185", +"payment_totals": "e75b3088ecccbf453f472518", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "e6d82b48a7a5593bd747ea69", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"f877c81e569d46b2f31adb8a" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "7ebef6bac8d0a4ff15fca702", +"orders_model_10": "07a1b43e2e1aec3a941c7463", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "f28ca9cbe4f0b82139ce42ff", +"orders_model_13": "8bfac0eb02357c4ef7316ba0", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "3af96276ee92b913506618b2", +"orders_model_17": "185a865c8cf0d67b3027a313", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "0c798fa3e6d807bf94e96eee", +"orders_model_21": "88425997d70bb43627dba42c", +"orders_model_22": "083e6ee404f8943487416adb", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "52f7f68c35fc1813f86aee3b", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_5": "1e772c63e569addeecf15a27", +"orders_model_6": "79cac6cd1f2e605da47e460d", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "653dbee5db7422883147ecf8", +"orders_model_9": "982d87b5a15b54ee70c70755", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "b2268fc29a60313e3b8c34ed", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"3eb464018675acfdd12a461f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "18de106324e6bd649e9a0662", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "68b962a61fcbd39f299bf7b9", +"orders_model_12": "31d217ac4f1c012c19d14c40", +"orders_model_13": "1d827eced23366a37ddba6b8", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "d1fb37ffa36108edc2a01aba", +"orders_model_16": "39a96779a764101f7d1dbae2", +"orders_model_17": "185a865c8cf0d67b3027a313", +"orders_model_18": "02d5138c4e7513182fc7cd52", +"orders_model_19": "3f36107952adc3d06995d9db", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "0c798fa3e6d807bf94e96eee", +"orders_model_21": "08e7a885c1d9662b2678c3ea", +"orders_model_22": "1b893dcaf72edc288a3b9710", +"orders_model_23": "f35475b4e5e4ae85ae46e91d", +"orders_model_3": "85fda65b19f7375e90342fa9", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_5": "949207159f621de6e270aed5", +"orders_model_6": "60e19e6908bb5c06d38eeb01", +"orders_model_7": "07289e08df177aa672b5da80", +"orders_model_8": "c18f5ec77f58bbb47cc78168", +"orders_model_9": "05bfc38d1bf1cc771029edec", +"payment_totals": "692528d52a5623a0e90d7fb4", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "0b5af099963f99173031a52f", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"8808b0d353112b97c19536d1" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json new file mode 100644 index 0000000000..58e8e5e046 --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json @@ -0,0 +1,1250 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "01c88e0760e09990ec7e7f27", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "0414cf2aabffee9324ca1152", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "0ffc9fd6d1c6d34c2db07849", +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "b1970df938e25e00faeed015", +"orders_model_10": "6a9dd6631afe2aecba420032", +"orders_model_11": "e0a1bcae33bbfb164c9468f6", +"orders_model_12": "926f7b801edc86d0d0ecbac0", +"orders_model_13": "8af6ca52fb84b5b173b2326d", +"orders_model_14": "c1ec6ecf89992261001a255f", +"orders_model_15": "cc32a1a5e07802f1512706b9", +"orders_model_16": "16ae4650da78bf9861f65553", +"orders_model_17": "ee556ea5f790f1d885cee84a", +"orders_model_18": "241cc7616b413ea893ca6829", +"orders_model_19": "92a8e66d811c1497a2216783", +"orders_model_2": "345f166deff852e9116d8801", +"orders_model_20": "97c09ef764382c456089a08f", +"orders_model_21": "29e669ac95b4e44f7d8ea530", +"orders_model_22": "8d9a8370c5bb21b381f42208", +"orders_model_23": "fe73a080f7f7a3e1df6c305c", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "10c324ddebd8f360f30b0a6b", +"orders_model_5": "0209faf6600f5936ca0209a7", +"orders_model_6": "6d3a0df899304a5fff38b3f0", +"orders_model_7": "2cc4699fcfbfc50f29d34e4d", +"orders_model_8": "82c19ad1bdebebf9b9e99f2c", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ffb88803fba96f5e1a0cc14", +"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"3f652f722b3dc3980f07f59d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "0897bc61ca6a0042539889cd", +"orders_model_10": "6a9dd6631afe2aecba420032", +"orders_model_11": "e0a1bcae33bbfb164c9468f6", +"orders_model_12": "fd707ea6f93871b0dc83c579", +"orders_model_13": "74b30199b5f8cc665f133e68", +"orders_model_14": "c1ec6ecf89992261001a255f", +"orders_model_15": "18719f2a40e9a0d55dce1f6d", +"orders_model_16": "5464f219a5381063a5cf2965", +"orders_model_17": "afd6d68f0a0a6689404e0260", +"orders_model_18": "ff71888baeb777eb8b0d274d", +"orders_model_19": "92a8e66d811c1497a2216783", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "97c09ef764382c456089a08f", +"orders_model_21": "af5f39f23b7c2b58d3f12930", +"orders_model_22": "3abd2e6a08df2cf8322bf309", +"orders_model_23": "fe73a080f7f7a3e1df6c305c", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "c868aee07ab0f13c50fadca2", +"orders_model_5": "8ca37d6ebd3f7cafc5b226da", +"orders_model_6": "7431806bd67f9e0c952a98ab", +"orders_model_7": "2cc4699fcfbfc50f29d34e4d", +"orders_model_8": "82c19ad1bdebebf9b9e99f2c", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ffb88803fba96f5e1a0cc14", +"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"4b773261b99e3dd24d7e41a5" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"9e3cc9a00de191a2a3a3a27f" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "01c88e0760e09990ec7e7f27", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "0414cf2aabffee9324ca1152", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "0ffc9fd6d1c6d34c2db07849", +"orders_model_0": "b09efd1e7181db7773253200", +"orders_model_1": "8f27fcd3be57b9a85a5ade04", +"orders_model_10": "b57ee1c35cbaadb688298310", +"orders_model_11": "1c545871d01b68d8cc5270d4", +"orders_model_12": "a2d666e8a32f9f36556fefca", +"orders_model_13": "356f32e628d61db27bf4a4f3", +"orders_model_14": "5770c08feedcc514f8cdea74", +"orders_model_15": "511a913bdc473948a1d98b00", +"orders_model_16": "7a40989a4e6cf23c222525f6", +"orders_model_17": "74820176e511b43c55d5dcb7", +"orders_model_18": "6162b5c431b89f8917de6793", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "3d131ea2ed9d90f4404800c4", +"orders_model_20": "24da7733eb4155182f26f868", +"orders_model_21": "afdbc811f2683769c6065622", +"orders_model_22": "1ecee061003eefdd8e7b3e1d", +"orders_model_23": "ebf7fe799455ca0ef8c2a02e", +"orders_model_3": "c3a07511c03df1968f2c5ff9", +"orders_model_4": "0eca13897e0abb4131837a39", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "8ed658a7454aba1b437c36b2", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "a0fc71fac4e12ec905ae953c", +"orders_model_9": "f6f4f76cf2077ee259b60302", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "78dd4b3d90301ccd1e781295", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "ae6c2278feec076b8e8bc0d9", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"e9da95982504131dc773c046" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "dc7f3dec778fd7cc3b47de47", +"orders_model_1": "881ca8c2aa83a640eb0c27ab", +"orders_model_10": "0498564b827ff224871c5c16", +"orders_model_11": "1c545871d01b68d8cc5270d4", +"orders_model_12": "a9609afdd975480f9b30d313", +"orders_model_13": "e9a825f1c611bead0b731f83", +"orders_model_14": "5770c08feedcc514f8cdea74", +"orders_model_15": "511a913bdc473948a1d98b00", +"orders_model_16": "00e981e5c6d50e8e603b21d1", +"orders_model_17": "3397814b3d1156e0e851a303", +"orders_model_18": "81452dd48c4fac8e75395e03", +"orders_model_19": "4782036882204544bf9b8cc0", +"orders_model_2": "3d131ea2ed9d90f4404800c4", +"orders_model_20": "d5ad421755fa576851cd7eea", +"orders_model_21": "afdbc811f2683769c6065622", +"orders_model_22": "82ea73f8ec2b48e09123b28a", +"orders_model_23": "ebf7fe799455ca0ef8c2a02e", +"orders_model_3": "c3a07511c03df1968f2c5ff9", +"orders_model_4": "bf62990d9480cf71956fd3c9", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "2694202ab29ccd1ad221d427", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "a0fc71fac4e12ec905ae953c", +"orders_model_9": "dadc55d42681e2d084d2c52b", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "78dd4b3d90301ccd1e781295", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "ae6c2278feec076b8e8bc0d9", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"d9d2cf58d167ce9056b86b8b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "01c88e0760e09990ec7e7f27", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "0414cf2aabffee9324ca1152", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "0ffc9fd6d1c6d34c2db07849", +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "1cbed5d338fb3ae061486bf7", +"orders_model_10": "5319ece7f82596acc6513f91", +"orders_model_11": "ccf2498a5b3526260f88df6f", +"orders_model_12": "cb6d3e8487b533349a2bad04", +"orders_model_13": "6c48a8656e02aa11445643e2", +"orders_model_14": "3c574035ecba3d30a27e46ce", +"orders_model_15": "530fb4a941e847207cba2410", +"orders_model_16": "a249acc516a880c9fbe25e88", +"orders_model_17": "7c698d2731cea40e031c9052", +"orders_model_18": "6d3a0df899304a5fff38b3f0", +"orders_model_19": "2cc4699fcfbfc50f29d34e4d", +"orders_model_2": "59a5d7fd62d3554715ee8ef0", +"orders_model_20": "1475a844903645d3154efcef", +"orders_model_21": "1d0e1910cfa54e2cf1cdf738", +"orders_model_22": "e1c0902ddf75d8f0324e3aeb", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "7db0fac3e405cbc04ce12b67", +"orders_model_4": "3cebd1bd3941ee9edc8ae06e", +"orders_model_5": "0e62e59d1dbc1c842380f957", +"orders_model_6": "6d3a0df899304a5fff38b3f0", +"orders_model_7": "dfa55753fe76745de6964097", +"orders_model_8": "bdc843833774d4e2ce081a50", +"orders_model_9": "f6f4f76cf2077ee259b60302", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8d7a74ec77a418a6a2641b84", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "b2268fc29a60313e3b8c34ed", +"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"a72c104311c712bf9eada7e3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "1cbed5d338fb3ae061486bf7", +"orders_model_10": "d02e5f6991f3149f83f298e7", +"orders_model_11": "ccf2498a5b3526260f88df6f", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "c6fe9d63cebac8a82557647b", +"orders_model_14": "3c574035ecba3d30a27e46ce", +"orders_model_15": "530fb4a941e847207cba2410", +"orders_model_16": "d545cb7b8ca26b55d32439c0", +"orders_model_17": "7c698d2731cea40e031c9052", +"orders_model_18": "7431806bd67f9e0c952a98ab", +"orders_model_19": "2cc4699fcfbfc50f29d34e4d", +"orders_model_2": "808d76e03e1b6ed06eb32fe2", +"orders_model_20": "cb78e3704fcaf1883d3f635d", +"orders_model_21": "1d0e1910cfa54e2cf1cdf738", +"orders_model_22": "a431a1c7e46ac0fd16f20095", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "7db0fac3e405cbc04ce12b67", +"orders_model_4": "3d86c54cb0dcd3d2cb0b7792", +"orders_model_5": "9a08a024fd692ca500ba98be", +"orders_model_6": "7431806bd67f9e0c952a98ab", +"orders_model_7": "35db1baf4dd2d3ddfefa9829", +"orders_model_8": "bdc843833774d4e2ce081a50", +"orders_model_9": "5748db46e2a60c5fb4056bfc", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8d7a74ec77a418a6a2641b84", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "af151ba34f16c141fdee1ac3", +"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"9d696237ea0f619ed4dee48f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json new file mode 100644 index 0000000000..a2f20ee860 --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json @@ -0,0 +1,1250 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "0c505e7c91961ed939fa9101", +"orders_model_10": "6a9dd6631afe2aecba420032", +"orders_model_11": "e0a1bcae33bbfb164c9468f6", +"orders_model_12": "926f7b801edc86d0d0ecbac0", +"orders_model_13": "8af6ca52fb84b5b173b2326d", +"orders_model_14": "c1ec6ecf89992261001a255f", +"orders_model_15": "b381e86843932813f7ad5b91", +"orders_model_16": "16ae4650da78bf9861f65553", +"orders_model_17": "ee556ea5f790f1d885cee84a", +"orders_model_18": "ff71888baeb777eb8b0d274d", +"orders_model_19": "92a8e66d811c1497a2216783", +"orders_model_2": "b1cffebb5797fe684c20e885", +"orders_model_20": "839fac6b32ebf58d515e9451", +"orders_model_21": "29e669ac95b4e44f7d8ea530", +"orders_model_22": "8d9a8370c5bb21b381f42208", +"orders_model_23": "fe73a080f7f7a3e1df6c305c", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "10c324ddebd8f360f30b0a6b", +"orders_model_5": "3fc282000bb67e74433ffd40", +"orders_model_6": "e813ee34de06d5cae82ac8c2", +"orders_model_7": "2cc4699fcfbfc50f29d34e4d", +"orders_model_8": "82c19ad1bdebebf9b9e99f2c", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ffb88803fba96f5e1a0cc14", +"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"e2fa30056701edaf7a347f5b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "0897bc61ca6a0042539889cd", +"orders_model_10": "6a9dd6631afe2aecba420032", +"orders_model_11": "e0a1bcae33bbfb164c9468f6", +"orders_model_12": "fd707ea6f93871b0dc83c579", +"orders_model_13": "74b30199b5f8cc665f133e68", +"orders_model_14": "c1ec6ecf89992261001a255f", +"orders_model_15": "7f3924ba267d897cc875a294", +"orders_model_16": "5464f219a5381063a5cf2965", +"orders_model_17": "afd6d68f0a0a6689404e0260", +"orders_model_18": "ff71888baeb777eb8b0d274d", +"orders_model_19": "92a8e66d811c1497a2216783", +"orders_model_2": "b1cffebb5797fe684c20e885", +"orders_model_20": "839fac6b32ebf58d515e9451", +"orders_model_21": "af5f39f23b7c2b58d3f12930", +"orders_model_22": "3abd2e6a08df2cf8322bf309", +"orders_model_23": "fe73a080f7f7a3e1df6c305c", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "c868aee07ab0f13c50fadca2", +"orders_model_5": "3fc282000bb67e74433ffd40", +"orders_model_6": "7431806bd67f9e0c952a98ab", +"orders_model_7": "2cc4699fcfbfc50f29d34e4d", +"orders_model_8": "82c19ad1bdebebf9b9e99f2c", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8ffb88803fba96f5e1a0cc14", +"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"73e2d7913705a099c6c0de21" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"9e3cc9a00de191a2a3a3a27f" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c0a4a49f398790b6968ded9b", +"orders_model_1": "8f27fcd3be57b9a85a5ade04", +"orders_model_10": "b57ee1c35cbaadb688298310", +"orders_model_11": "1c545871d01b68d8cc5270d4", +"orders_model_12": "a7868c059b8f4ff1fe264c2a", +"orders_model_13": "356f32e628d61db27bf4a4f3", +"orders_model_14": "5770c08feedcc514f8cdea74", +"orders_model_15": "6d334b184def14c92a91f6fe", +"orders_model_16": "7a40989a4e6cf23c222525f6", +"orders_model_17": "74820176e511b43c55d5dcb7", +"orders_model_18": "864293c567900a908d92f817", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "3d131ea2ed9d90f4404800c4", +"orders_model_20": "9ea231ae7342ed67e393353e", +"orders_model_21": "afdbc811f2683769c6065622", +"orders_model_22": "1ecee061003eefdd8e7b3e1d", +"orders_model_23": "ebf7fe799455ca0ef8c2a02e", +"orders_model_3": "2136d13b1291a5e9dd31e887", +"orders_model_4": "0eca13897e0abb4131837a39", +"orders_model_5": "7a9fabf5b3d3ab9e1fc63a99", +"orders_model_6": "ea32e1b4eb22b26be9f440ed", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "a0fc71fac4e12ec905ae953c", +"orders_model_9": "f6f4f76cf2077ee259b60302", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "78dd4b3d90301ccd1e781295", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "ae6c2278feec076b8e8bc0d9", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"5566f9b034670ebc7d57c741" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "dc7f3dec778fd7cc3b47de47", +"orders_model_1": "881ca8c2aa83a640eb0c27ab", +"orders_model_10": "0498564b827ff224871c5c16", +"orders_model_11": "1c545871d01b68d8cc5270d4", +"orders_model_12": "1b5b1859d336c6311e9827f1", +"orders_model_13": "e9a825f1c611bead0b731f83", +"orders_model_14": "5770c08feedcc514f8cdea74", +"orders_model_15": "6d334b184def14c92a91f6fe", +"orders_model_16": "00e981e5c6d50e8e603b21d1", +"orders_model_17": "3397814b3d1156e0e851a303", +"orders_model_18": "81452dd48c4fac8e75395e03", +"orders_model_19": "4782036882204544bf9b8cc0", +"orders_model_2": "3d131ea2ed9d90f4404800c4", +"orders_model_20": "b69caa0299e13825ed690cb8", +"orders_model_21": "afdbc811f2683769c6065622", +"orders_model_22": "82ea73f8ec2b48e09123b28a", +"orders_model_23": "ebf7fe799455ca0ef8c2a02e", +"orders_model_3": "2136d13b1291a5e9dd31e887", +"orders_model_4": "bf62990d9480cf71956fd3c9", +"orders_model_5": "458d9725be39144ca5e0c33d", +"orders_model_6": "4359abfef23a7b1e646d2aee", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "a0fc71fac4e12ec905ae953c", +"orders_model_9": "dadc55d42681e2d084d2c52b", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "78dd4b3d90301ccd1e781295", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "ae6c2278feec076b8e8bc0d9", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"eff03c0ebcf2c6323671b7ee" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", +"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "1cbed5d338fb3ae061486bf7", +"orders_model_10": "5a31508076b33a63eb5f6f5a", +"orders_model_11": "ccf2498a5b3526260f88df6f", +"orders_model_12": "2d47664882f4e1df073137de", +"orders_model_13": "6c48a8656e02aa11445643e2", +"orders_model_14": "5324123258199836670bd610", +"orders_model_15": "27153ec6b977ca8e32094d2f", +"orders_model_16": "a249acc516a880c9fbe25e88", +"orders_model_17": "b29a2c85abeb6ad80d6890c3", +"orders_model_18": "e813ee34de06d5cae82ac8c2", +"orders_model_19": "2cc4699fcfbfc50f29d34e4d", +"orders_model_2": "3302ce3fd2e8a684f2be9632", +"orders_model_20": "e4ff6e5c3b52a9c5568a3ff1", +"orders_model_21": "1d0e1910cfa54e2cf1cdf738", +"orders_model_22": "e1c0902ddf75d8f0324e3aeb", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "80151fc977ccd29484c79a45", +"orders_model_4": "1fe47e48e7adf0e94261cf1d", +"orders_model_5": "f9183611419f32c78a12fbc4", +"orders_model_6": "e813ee34de06d5cae82ac8c2", +"orders_model_7": "dfa55753fe76745de6964097", +"orders_model_8": "bdc843833774d4e2ce081a50", +"orders_model_9": "f6f4f76cf2077ee259b60302", +"payment_totals": "ddedaaeda0bdb9d9997ac6cd", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8d7a74ec77a418a6a2641b84", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "b2268fc29a60313e3b8c34ed", +"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345", +"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +} +], +[ +"catalog", +"495c0f20724d3290a1990d2d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "dfd1ab68201bde357955ba7f", +"adapter_rules_staging": "1647edbe8ac61356261cfa76", +"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", +"dotless_i_staging": "4c25b6514c8939c1579e7729", +"long_s_cte": "22aa6898e4deee5e707a61c8", +"long_s_staging": "c70692ed48ec89dc344df4e0", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "1cbed5d338fb3ae061486bf7", +"orders_model_10": "d02e5f6991f3149f83f298e7", +"orders_model_11": "ccf2498a5b3526260f88df6f", +"orders_model_12": "a030043d592af380cf44cb9f", +"orders_model_13": "c6fe9d63cebac8a82557647b", +"orders_model_14": "5324123258199836670bd610", +"orders_model_15": "27153ec6b977ca8e32094d2f", +"orders_model_16": "d545cb7b8ca26b55d32439c0", +"orders_model_17": "b29a2c85abeb6ad80d6890c3", +"orders_model_18": "7431806bd67f9e0c952a98ab", +"orders_model_19": "2cc4699fcfbfc50f29d34e4d", +"orders_model_2": "3302ce3fd2e8a684f2be9632", +"orders_model_20": "e4ff6e5c3b52a9c5568a3ff1", +"orders_model_21": "1d0e1910cfa54e2cf1cdf738", +"orders_model_22": "a431a1c7e46ac0fd16f20095", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "80151fc977ccd29484c79a45", +"orders_model_4": "3d86c54cb0dcd3d2cb0b7792", +"orders_model_5": "f9183611419f32c78a12fbc4", +"orders_model_6": "7431806bd67f9e0c952a98ab", +"orders_model_7": "35db1baf4dd2d3ddfefa9829", +"orders_model_8": "bdc843833774d4e2ce081a50", +"orders_model_9": "5748db46e2a60c5fb4056bfc", +"payment_totals": "ce8a97426d53e0acd6c49c2b", +"payments_by_customer": "da1968ec91b55f6f584c7ecd", +"pivot_0": "8d7a74ec77a418a6a2641b84", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "af151ba34f16c141fdee1ac3", +"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe", +"umlaut_cte": "a4c1810f1e46ef2200a87309" +} +], +[ +"catalog", +"d2a0f4ec04fb9eac758c1993" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json new file mode 100644 index 0000000000..b3c3149315 --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json @@ -0,0 +1,3458 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "6f741725ae5781425358bc3b", +"orders_model_10": "c7e95ff7d90362e121ab238f", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "15537f706967682b808641f6", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "3dbaddb57a2ed5f8db44feda", +"orders_model_17": "7cc87d8c32df82057dc6057d", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "4782036882204544bf9b8cc0", +"orders_model_2": "27b686e0958c19d151cd6319", +"orders_model_20": "12e4c14a088d64a94a267025", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "9a08a024fd692ca500ba98be", +"orders_model_6": "526c167cb476b3553de71042", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "800e86b4e6a207eda6a59688", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"6232f045280d66a7b86567e9" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"ff4d4f462aae3b9e95d9a5ab" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"e718df74c851405aa9e05532" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "6f741725ae5781425358bc3b", +"orders_model_10": "c7e95ff7d90362e121ab238f", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "462bf85d3521fa64063b2e26", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "3dbaddb57a2ed5f8db44feda", +"orders_model_17": "7cc87d8c32df82057dc6057d", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "27b686e0958c19d151cd6319", +"orders_model_20": "83395d349a292162557b74ba", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "07dc61a646b8222df8eccb85", +"orders_model_6": "526c167cb476b3553de71042", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "e88481648db54aa4b3e3b0d6", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"b316f125f0e7703dd9e37065" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "1c5f7e1a73535a5617fb070a", +"orders_model_1": "ff37aa9e31c0ddbc7f05cba2", +"orders_model_10": "315f07530cae631f3e373e9d", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_12": "94f95487333b2561382c2981", +"orders_model_13": "f73e95ff8968d95ed11dc4f3", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "1a6c68f8be75e24bf3304cdb", +"orders_model_17": "2b9d7cab1383a6532589c1ae", +"orders_model_18": "b1a0e72c622ac940a7b5fb00", +"orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_2": "27b686e0958c19d151cd6319", +"orders_model_20": "207a0ee79521c1a9b4effb31", +"orders_model_21": "c669dc21fed757d7809fc424", +"orders_model_22": "066b8f5bf2be63a888045fe7", +"orders_model_23": "14b86bcb1a8edc065f4c7405", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "0e62e59d1dbc1c842380f957", +"orders_model_6": "c72d1a9250666a5ac7e54369", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_8": "8454628161253c1b281bb22f", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"b749d066f2cc882b93817838" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "9b0cc36921b5b8b8a8dea025", +"orders_model_10": "d4f810ff3ba14295385d6fb9", +"orders_model_11": "f7cdb34524d45acad3b71f7d", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_13": "9f0321d97fb59396dfa496d9", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_16": "c5a645b8cfb02ddc927fc297", +"orders_model_17": "669b30fefd09621ed0f2ad55", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_19": "5318b049f2f651a689fb5e87", +"orders_model_2": "361de61cfd22f09af946862f", +"orders_model_20": "032990c645506e1e133c4cd0", +"orders_model_21": "9c26876fe71c235386932362", +"orders_model_22": "86880e31d61c6c5522256c62", +"orders_model_23": "209048028aa76bf4c2bd28c6", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "e672120be8dbea0cda9c4f28", +"orders_model_6": "a903f36871fea6d8ac818922", +"orders_model_7": "a4c400c5fa2da4107098339e", +"orders_model_8": "aba445941b1d72b9d3d5acb6", +"orders_model_9": "67dc0f1b5ce04c8989ad1aca", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "335dfa47b776524b62ee26e6", +"pivot_1": "78dd4b3d90301ccd1e781295", +"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "826e0ad9aec9b04097d58b37", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"9c7db1355dd2394241b43e81" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "73d5e99497d80b919e0d5491", +"orders_model_10": "31ba43d1ed6f0701481c543b", +"orders_model_11": "ac182afebaeb1ab5a7b50a67", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_13": "3676088fa7438b6033da7334", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_16": "3122402ab671f4438c8209d7", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "3a3bfc6019ae1cf18c714dd0", +"orders_model_20": "c2da59cc5e833d3421094ea9", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "866b4de40862e3cc32f54936", +"orders_model_23": "79c750e0d8bd9a28f05f6337", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "72717ea55695667a7b7d8b39", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"a27824cf887c032c6fee964b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"24ec11d3b54173b8b7b769ff" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "73d5e99497d80b919e0d5491", +"orders_model_10": "8ef67d7c6546651d2f2e0b45", +"orders_model_11": "ac182afebaeb1ab5a7b50a67", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_13": "cc456ea2e83298c9cd8206fb", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_16": "a6740f24eddec216a1afc653", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "3a3bfc6019ae1cf18c714dd0", +"orders_model_20": "c2da59cc5e833d3421094ea9", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "866b4de40862e3cc32f54936", +"orders_model_23": "79c750e0d8bd9a28f05f6337", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "5e48e2da1954c3893344d4a8", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "e6d82b48a7a5593bd747ea69", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"4362f040d5943b99ffdc354f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "1c5f7e1a73535a5617fb070a", +"orders_model_1": "828d747058993aeefdbae18a", +"orders_model_10": "83266a86a028c32724e8cd26", +"orders_model_11": "ac182afebaeb1ab5a7b50a67", +"orders_model_12": "2cbf8e290082ab49db794db4", +"orders_model_13": "3676088fa7438b6033da7334", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_16": "b67d418ac9424b16909d6a26", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "5d2a8162e9742273846f69e3", +"orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_2": "3a3bfc6019ae1cf18c714dd0", +"orders_model_20": "c2da59cc5e833d3421094ea9", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_22": "6e4d8e3c08d5ad97e417c6f7", +"orders_model_23": "79c750e0d8bd9a28f05f6337", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", +"orders_model_6": "ade3350d63ecf8b7583ca5cb", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_8": "e25266f92d285b11b68983a6", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"db139e73f95bffd34e7a6de5" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "3e1948d706ff1cecf42f7863", +"orders_model_10": "fbdacfcd4b561abe157cdfda", +"orders_model_11": "17515e5f7babd19ac587b1d3", +"orders_model_12": "b06fcf5168d718f9042eb9f0", +"orders_model_13": "25952bed7c264fceaeec357c", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_16": "b67d418ac9424b16909d6a26", +"orders_model_17": "769e25e737cf7832171d8837", +"orders_model_18": "8a322bf0beb93c0034bd28d5", +"orders_model_19": "a4c400c5fa2da4107098339e", +"orders_model_2": "2f584b5185d90ee4c66c8339", +"orders_model_20": "c2da59cc5e833d3421094ea9", +"orders_model_21": "67dc0f1b5ce04c8989ad1aca", +"orders_model_22": "866b4de40862e3cc32f54936", +"orders_model_23": "1c16922430c6790814d0a7df", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "2f2cbf190f07bea2687a7a1e", +"orders_model_6": "b8c7c62baf6528acb751f82c", +"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", +"orders_model_8": "70f77b7d63c1bbe6e602e9bb", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "db45532b4aa0746bb6155579", +"pivot_1": "826e0ad9aec9b04097d58b37", +"pivot_2": "78dd4b3d90301ccd1e781295", +"pivot_3": "0b5af099963f99173031a52f", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"b83e75232474640290815c9d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "dc7f3dec778fd7cc3b47de47", +"orders_model_1": "98e73c916923e6ad3dbb4fe8", +"orders_model_10": "bba235f3ba5a071d31c89cfc", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "2ab277361bc756f8553cd4fc", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_16": "bf62990d9480cf71956fd3c9", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "c4c391b60a4436e3a6a9105e", +"orders_model_20": "5660541da0116a33de85cf37", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "9a483c2d5942e8857be41233", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"b2b2221f2da5d47b86b56575" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"ff4d4f462aae3b9e95d9a5ab" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "b21bb72c9b7652fb99f52bf8", +"orders_model_1": "98e73c916923e6ad3dbb4fe8", +"orders_model_10": "bba235f3ba5a071d31c89cfc", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_16": "24d75028ccc9de7633328c96", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "5660541da0116a33de85cf37", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "692f18d932cd76c441a79e81", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"3a861a8c7cc6422ec893ea77" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "b09efd1e7181db7773253200", +"orders_model_1": "f8df97502ce8987ade2b8ae6", +"orders_model_10": "d9e87a16e347f6f0316452d6", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "1c5f7e1a73535a5617fb070a", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_16": "0eca13897e0abb4131837a39", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "74eb746be2a4278dae684968", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "5660541da0116a33de85cf37", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "83e085c3265373904422aad9", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "c88625738d77bd28714c4086", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "ec0818249ce5c1a3abdbba12", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"01ad0f760ce6f305ea550857" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "36669f325385a145cfe71921", +"orders_model_1": "45de3ae2081edb1155411120", +"orders_model_10": "bba235f3ba5a071d31c89cfc", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_12": "dc4da2070db56afde4157773", +"orders_model_13": "87a319b5caf7cc32bc6aa50e", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_16": "8ef4aea858f63561f24ec68b", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "8cf38d3ff37cd8143561920a", +"orders_model_19": "dcb9d89d4b588b53682fb0b4", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "5660541da0116a33de85cf37", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_23": "857c9a58882bddd68cec3bd5", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "0541825c1a3bbc51c23f3a53", +"orders_model_7": "526a9e7adadbf085a39764b5", +"orders_model_8": "3bf1aff8cb84ab7999a0b5fd", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "0b5af099963f99173031a52f", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"032717ee1f3f581899d54bf7" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "dc7f3dec778fd7cc3b47de47", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "f386cc5235b54c550f4924f8", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "36c156c5a6601badf8124464", +"orders_model_7": "ef0eb5ded0428f49ac6cb411", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"4f98b4e1b4999a33ba1ff099" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "b21bb72c9b7652fb99f52bf8", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "f386cc5235b54c550f4924f8", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "777e18bfdcc2423b67211d39", +"orders_model_7": "80a1f1b7b38c1afa802d7625", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "e6d82b48a7a5593bd747ea69", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"055d801ba49e61e8a1e414b2" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "1c5f7e1a73535a5617fb070a", +"orders_model_1": "673e75c341c3f2437091de09", +"orders_model_10": "0df969272aa19a78a705bc92", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "b09efd1e7181db7773253200", +"orders_model_13": "bc666dca3ca9016f640dfe68", +"orders_model_14": "7c3ab71786810446d45e97a3", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "cf4db989e60b4ff2ee7b8b90", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "b1a0e72c622ac940a7b5fb00", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "59228560ec9692b57930d8b5", +"orders_model_22": "b8238ed1e26320d8f8912882", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "1cefa36f28721fe677e6a684", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "3197497d139b9241f4b6e04c", +"orders_model_7": "44dc5de3912695d88a818a03", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "b2268fc29a60313e3b8c34ed", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"74156f1f4b47943ba3b769dd" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "dc4da2070db56afde4157773", +"orders_model_1": "b6345243b4bb0009ec2dfb5d", +"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_12": "36669f325385a145cfe71921", +"orders_model_13": "87a1dbf609e37b67c90e35b2", +"orders_model_14": "544d358c786985653996181d", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "cf4db989e60b4ff2ee7b8b90", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_19": "ab249088d32d341a013be687", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "176a688a47e60b55e1ce0673", +"orders_model_22": "c6255585e2f5156964d8f8f8", +"orders_model_23": "e0ec1521e19c47996421ec94", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "c2b91215ea727d9313a22740", +"orders_model_7": "afb5b3102eaac7fa202355c0", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "283c165f2da9102ffec377c4", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "0b5af099963f99173031a52f", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"3d1560ed382e916606e5f85f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "abadf69a0572399b6536a2c1", +"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "278d4fd453aea2eca14894ea", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "5ccadf27b4bf1a37e0f30682", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "12b73be6a6b5985f71de1ca5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "91d92b539582dec7737d1704", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "e29709e8e7acb44cc782304f", +"orders_model_5": "afd6d68f0a0a6689404e0260", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "6be36f8074a475ab3f5bce6c", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"c7b70a4fc469694d9d64eff8" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "a23763739c27a47ae12ebed2", +"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "56d09679844a976541cce33c", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "f73c7edc9f35cb7956a40b52", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "12b73be6a6b5985f71de1ca5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "2853fca6c5782a3d8f7f3229", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "c8c4091a11671ad7035944d6", +"orders_model_5": "afd6d68f0a0a6689404e0260", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "8a583d5a0fe333e4583746b2", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"8cc95852afc3fab17c43340c" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "ab25f3ba101f081f8a3f936c", +"orders_model_10": "3aec7a059badfa0e968d5216", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_12": "cb6d3e8487b533349a2bad04", +"orders_model_13": "d55e6b7bb6533cea41bcfeda", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_16": "97e322d50d70c30c61fb7a15", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "32b8af78b243949e0198fda6", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "345f166deff852e9116d8801", +"orders_model_20": "12b73be6a6b5985f71de1ca5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "368c97dc5eea624b492ce042", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "7565624f8000d1c0566d8ed3", +"orders_model_5": "ee556ea5f790f1d885cee84a", +"orders_model_6": "5d2a8162e9742273846f69e3", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "c7d3f9e5025dad577c958a44", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"08a4d5d6c6bf5e75b2e23d81" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "c7630eba442c12475a304749", +"orders_model_10": "fa26ab8510354dd670525ac2", +"orders_model_11": "eb7d097be111399bcd2f940f", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", +"orders_model_14": "273c339ab90e9a64a7c8f8a5", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_16": "e018db5f6e3eaf8888bd8eff", +"orders_model_17": "fe03c6a89b870943b6dd5922", +"orders_model_18": "fd5138ac4af02cd168a423e6", +"orders_model_19": "e24f113150b49c7e955cf81b", +"orders_model_2": "daca571af4a4d1cb1afbce74", +"orders_model_20": "12b73be6a6b5985f71de1ca5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "24bdd574c66fd2f692429cfb", +"orders_model_23": "e94cdbeab01617cd05a55f82", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "a908ebc07c10d1423a970a49", +"orders_model_5": "7af136b7e522ccf14f427e1e", +"orders_model_6": "8a322bf0beb93c0034bd28d5", +"orders_model_7": "4983a5e9302c8b9e28e75202", +"orders_model_8": "8a583d5a0fe333e4583746b2", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "0b5af099963f99173031a52f", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"f19e292dff42d4c08b5da8ad" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "c909142a40bfa94a0849e672", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "159cce49ef0548e95a5b46fe", +"orders_model_13": "ba7bafc812a816c0c0770d82", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "e29709e8e7acb44cc782304f", +"orders_model_17": "40ca546bd222f4bd18e62461", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "4a708ce5c2d7f50400bef81e", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_5": "efc04632c3bd90f5ca705b61", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "653dbee5db7422883147ecf8", +"orders_model_9": "e2d21c84b2b62dea98a1d185", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "af151ba34f16c141fdee1ac3", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"f877c81e569d46b2f31adb8a" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "c909142a40bfa94a0849e672", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "eb36534deed90e9e2fb062de", +"orders_model_13": "b929a7472b014a9b5b757127", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "c8c4091a11671ad7035944d6", +"orders_model_17": "40ca546bd222f4bd18e62461", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "4a708ce5c2d7f50400bef81e", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_5": "efc04632c3bd90f5ca705b61", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "85993844a344d5d4408276d0", +"orders_model_9": "e2d21c84b2b62dea98a1d185", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "e6d82b48a7a5593bd747ea69", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"f877c81e569d46b2f31adb8a" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "d69e281f2ec58a1c2cfc8e75", +"orders_model_10": "07a1b43e2e1aec3a941c7463", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_12": "e3d5141e99608c2ca5d0df9b", +"orders_model_13": "8bfac0eb02357c4ef7316ba0", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "7565624f8000d1c0566d8ed3", +"orders_model_17": "185a865c8cf0d67b3027a313", +"orders_model_18": "4ed6b441d50e250d458a67ec", +"orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "59129bc02b2fd6f6f8bfb348", +"orders_model_21": "88425997d70bb43627dba42c", +"orders_model_22": "14091564e5091161047f133c", +"orders_model_23": "6423a33f3457191fa1b24540", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_5": "1e772c63e569addeecf15a27", +"orders_model_6": "b1a0e72c622ac940a7b5fb00", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_8": "653dbee5db7422883147ecf8", +"orders_model_9": "982d87b5a15b54ee70c70755", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "b2268fc29a60313e3b8c34ed", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"3eb464018675acfdd12a461f" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "18de106324e6bd649e9a0662", +"orders_model_10": "99f0746f249fd25788f1348a", +"orders_model_11": "68b962a61fcbd39f299bf7b9", +"orders_model_12": "bd31b96a3fa2e1bb80699d6e", +"orders_model_13": "1d827eced23366a37ddba6b8", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_16": "a908ebc07c10d1423a970a49", +"orders_model_17": "185a865c8cf0d67b3027a313", +"orders_model_18": "02d5138c4e7513182fc7cd52", +"orders_model_19": "3f36107952adc3d06995d9db", +"orders_model_2": "be51e7ded79e692b0f86ad8e", +"orders_model_20": "59129bc02b2fd6f6f8bfb348", +"orders_model_21": "08e7a885c1d9662b2678c3ea", +"orders_model_22": "1b893dcaf72edc288a3b9710", +"orders_model_23": "f35475b4e5e4ae85ae46e91d", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_5": "949207159f621de6e270aed5", +"orders_model_6": "60e19e6908bb5c06d38eeb01", +"orders_model_7": "07289e08df177aa672b5da80", +"orders_model_8": "4fe24f5b9f54838716b41386", +"orders_model_9": "05bfc38d1bf1cc771029edec", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"pivot_0": "0b5af099963f99173031a52f", +"pivot_1": "bd6145283beb468ce86b460b", +"pivot_2": "db45532b4aa0746bb6155579", +"pivot_3": "5cf2515c32c9c98b0a9d8315", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"23b5038345131be7d003ad7b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +] +] diff --git a/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json b/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json new file mode 100644 index 0000000000..6deaca5f2b --- /dev/null +++ b/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json @@ -0,0 +1,148 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"missing_east": "aba5ab28f3a97ef8fce3c485", +"missing_north": "777ae26d6fa90296228d39f8", +"missing_west": "8a38648fbc0412a83d2626ef", +"orders_east": "4784ce99683381a7cb1bbeca", +"orders_north": "4784ce99683381a7cb1bbeca", +"orders_south": "4784ce99683381a7cb1bbeca", +"orders_summary": "1bda84c5cb7bff52c8d05569", +"orders_west": "4784ce99683381a7cb1bbeca", +"totals_east": "77d2a746f8facada159be569", +"totals_north": "a8d32a96d12ea19c455c183d", +"totals_south": "3b15726cb112aac93fc49acf", +"totals_west": "2b5d9cbefea62a4be70e381b" +} +], +[ +"catalog", +"9c499b98266c28cea392795d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"missing_east": "aba5ab28f3a97ef8fce3c485", +"missing_north": "777ae26d6fa90296228d39f8", +"missing_west": "8a38648fbc0412a83d2626ef", +"orders_east": "4784ce99683381a7cb1bbeca", +"orders_north": "4784ce99683381a7cb1bbeca", +"orders_south": "4784ce99683381a7cb1bbeca", +"orders_summary": "1bda84c5cb7bff52c8d05569", +"orders_west": "4784ce99683381a7cb1bbeca", +"totals_east": "77d2a746f8facada159be569", +"totals_north": "a8d32a96d12ea19c455c183d", +"totals_south": "3b15726cb112aac93fc49acf", +"totals_west": "2b5d9cbefea62a4be70e381b" +} +], +[ +"catalog", +"9c499b98266c28cea392795d" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +] +] diff --git a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json new file mode 100644 index 0000000000..443217bb53 --- /dev/null +++ b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json @@ -0,0 +1,722 @@ +[ +[ +"contracts", +{ +"None": "946a3061a7e72843e29b93c8", +"bigquery": "946a3061a7e72843e29b93c8", +"databricks": "946a3061a7e72843e29b93c8", +"duckdb": "946a3061a7e72843e29b93c8", +"postgres": "946a3061a7e72843e29b93c8", +"snowflake": "946a3061a7e72843e29b93c8", +"tsql": "946a3061a7e72843e29b93c8" +} +], +[ +"contracts", +{ +"None": "e298b23cdc712b436ba9140b", +"bigquery": "e298b23cdc712b436ba9140b", +"databricks": "e298b23cdc712b436ba9140b", +"duckdb": "e298b23cdc712b436ba9140b", +"postgres": "e298b23cdc712b436ba9140b", +"snowflake": "e298b23cdc712b436ba9140b", +"tsql": "e298b23cdc712b436ba9140b" +} +], +[ +"contracts", +{ +"None": "b3283bf184bb082f364b8537", +"bigquery": "b3283bf184bb082f364b8537", +"databricks": "b3283bf184bb082f364b8537", +"duckdb": "b3283bf184bb082f364b8537", +"postgres": "b3283bf184bb082f364b8537", +"snowflake": "b3283bf184bb082f364b8537", +"tsql": "b3283bf184bb082f364b8537" +} +], +[ +"contracts", +{ +"None": "b1e12cf6dc9584935dabf76a", +"bigquery": "b1e12cf6dc9584935dabf76a", +"databricks": "b1e12cf6dc9584935dabf76a", +"duckdb": "b1e12cf6dc9584935dabf76a", +"postgres": "b1e12cf6dc9584935dabf76a", +"snowflake": "b1e12cf6dc9584935dabf76a", +"tsql": "b1e12cf6dc9584935dabf76a" +} +], +[ +"contracts", +{ +"None": "e44260e0d2411c3cce8781cc", +"bigquery": "e44260e0d2411c3cce8781cc", +"databricks": "e44260e0d2411c3cce8781cc", +"duckdb": "e44260e0d2411c3cce8781cc", +"postgres": "e44260e0d2411c3cce8781cc", +"snowflake": "e44260e0d2411c3cce8781cc", +"tsql": "e44260e0d2411c3cce8781cc" +} +], +[ +"contracts", +{ +"None": "2ea2c17632881c11f524f96c", +"bigquery": "2ea2c17632881c11f524f96c", +"databricks": "2ea2c17632881c11f524f96c", +"duckdb": "2ea2c17632881c11f524f96c", +"postgres": "2ea2c17632881c11f524f96c", +"snowflake": "2ea2c17632881c11f524f96c", +"tsql": "2ea2c17632881c11f524f96c" +} +], +[ +"contracts", +{ +"None": "0b6e350a4f4afde493cb8626", +"bigquery": "0b6e350a4f4afde493cb8626", +"databricks": "0b6e350a4f4afde493cb8626", +"duckdb": "0b6e350a4f4afde493cb8626", +"postgres": "0b6e350a4f4afde493cb8626", +"snowflake": "0b6e350a4f4afde493cb8626", +"tsql": "0b6e350a4f4afde493cb8626" +} +], +[ +"contracts", +{ +"None": "ec97cc8e40b7631612395b21", +"bigquery": "ec97cc8e40b7631612395b21", +"databricks": "ec97cc8e40b7631612395b21", +"duckdb": "ec97cc8e40b7631612395b21", +"postgres": "ec97cc8e40b7631612395b21", +"snowflake": "ec97cc8e40b7631612395b21", +"tsql": "ec97cc8e40b7631612395b21" +} +], +[ +"contracts", +{ +"None": "a428eb672c78b30d5702cafd", +"bigquery": "a428eb672c78b30d5702cafd", +"databricks": "a428eb672c78b30d5702cafd", +"duckdb": "a428eb672c78b30d5702cafd", +"postgres": "a428eb672c78b30d5702cafd", +"snowflake": "a428eb672c78b30d5702cafd", +"tsql": "a428eb672c78b30d5702cafd" +} +], +[ +"contracts", +{ +"None": "839434d5b73abdf9499ad59e", +"bigquery": "839434d5b73abdf9499ad59e", +"databricks": "839434d5b73abdf9499ad59e", +"duckdb": "839434d5b73abdf9499ad59e", +"postgres": "839434d5b73abdf9499ad59e", +"snowflake": "839434d5b73abdf9499ad59e", +"tsql": "839434d5b73abdf9499ad59e" +} +], +[ +"contracts", +{ +"None": "ab9b0c5c3dd6f54aea71a5f1", +"bigquery": "ab9b0c5c3dd6f54aea71a5f1", +"databricks": "ab9b0c5c3dd6f54aea71a5f1", +"duckdb": "ab9b0c5c3dd6f54aea71a5f1", +"postgres": "ab9b0c5c3dd6f54aea71a5f1", +"snowflake": "ab9b0c5c3dd6f54aea71a5f1", +"tsql": "ab9b0c5c3dd6f54aea71a5f1" +} +], +[ +"contracts", +{ +"None": "d9c54c1a8d6089f971147bfc", +"bigquery": "d9c54c1a8d6089f971147bfc", +"databricks": "d9c54c1a8d6089f971147bfc", +"duckdb": "d9c54c1a8d6089f971147bfc", +"postgres": "d9c54c1a8d6089f971147bfc", +"snowflake": "d9c54c1a8d6089f971147bfc", +"tsql": "d9c54c1a8d6089f971147bfc" +} +], +[ +"contracts", +{ +"None": "48ff6f452c064d9c0d6f68ed", +"bigquery": "48ff6f452c064d9c0d6f68ed", +"databricks": "48ff6f452c064d9c0d6f68ed", +"duckdb": "48ff6f452c064d9c0d6f68ed", +"postgres": "48ff6f452c064d9c0d6f68ed", +"snowflake": "48ff6f452c064d9c0d6f68ed", +"tsql": "78d512867db744527fe3982b" +} +], +[ +"contracts", +{ +"None": "17c278499d9aad7922d610d5", +"bigquery": "17c278499d9aad7922d610d5", +"databricks": "17c278499d9aad7922d610d5", +"duckdb": "17c278499d9aad7922d610d5", +"postgres": "17c278499d9aad7922d610d5", +"snowflake": "17c278499d9aad7922d610d5", +"tsql": "17c278499d9aad7922d610d5" +} +], +[ +"contracts", +{ +"None": "c332cbe9d45bff687e91f93b", +"bigquery": "c332cbe9d45bff687e91f93b", +"databricks": "c332cbe9d45bff687e91f93b", +"duckdb": "c332cbe9d45bff687e91f93b", +"postgres": "c332cbe9d45bff687e91f93b", +"snowflake": "c332cbe9d45bff687e91f93b", +"tsql": "c332cbe9d45bff687e91f93b" +} +], +[ +"contracts", +{ +"None": "23893ae7f9f05f0777697eba", +"bigquery": "23893ae7f9f05f0777697eba", +"databricks": "23893ae7f9f05f0777697eba", +"duckdb": "23893ae7f9f05f0777697eba", +"postgres": "23893ae7f9f05f0777697eba", +"snowflake": "23893ae7f9f05f0777697eba", +"tsql": "23893ae7f9f05f0777697eba" +} +], +[ +"contracts", +{ +"None": "bcc0d54db4ee9d237ef10dd4", +"bigquery": "bcc0d54db4ee9d237ef10dd4", +"databricks": "bcc0d54db4ee9d237ef10dd4", +"duckdb": "bcc0d54db4ee9d237ef10dd4", +"postgres": "bcc0d54db4ee9d237ef10dd4", +"snowflake": "bcc0d54db4ee9d237ef10dd4", +"tsql": "bcc0d54db4ee9d237ef10dd4" +} +], +[ +"contracts", +{ +"None": "0d0a771fb0ff7b39fb021d4d", +"bigquery": "0d0a771fb0ff7b39fb021d4d", +"databricks": "0d0a771fb0ff7b39fb021d4d", +"duckdb": "0d0a771fb0ff7b39fb021d4d", +"postgres": "0d0a771fb0ff7b39fb021d4d", +"snowflake": "0d0a771fb0ff7b39fb021d4d", +"tsql": "0d0a771fb0ff7b39fb021d4d" +} +], +[ +"contracts", +{ +"None": "98b119ddd61340fcdf4830fb", +"bigquery": "98b119ddd61340fcdf4830fb", +"databricks": "98b119ddd61340fcdf4830fb", +"duckdb": "98b119ddd61340fcdf4830fb", +"postgres": "98b119ddd61340fcdf4830fb", +"snowflake": "98b119ddd61340fcdf4830fb", +"tsql": "98b119ddd61340fcdf4830fb" +} +], +[ +"contracts", +{ +"None": "f100476682030d396856d58d", +"bigquery": "f100476682030d396856d58d", +"databricks": "f100476682030d396856d58d", +"duckdb": "f100476682030d396856d58d", +"postgres": "f100476682030d396856d58d", +"snowflake": "f100476682030d396856d58d", +"tsql": "f100476682030d396856d58d" +} +], +[ +"contracts", +{ +"None": "d03a2acd92b5e17c753a9272", +"bigquery": "d03a2acd92b5e17c753a9272", +"databricks": "d03a2acd92b5e17c753a9272", +"duckdb": "d03a2acd92b5e17c753a9272", +"postgres": "d03a2acd92b5e17c753a9272", +"snowflake": "d03a2acd92b5e17c753a9272", +"tsql": "d03a2acd92b5e17c753a9272" +} +], +[ +"contracts", +{ +"None": "2d4b87c5b06ff335bd6c741b", +"bigquery": "2d4b87c5b06ff335bd6c741b", +"databricks": "2d4b87c5b06ff335bd6c741b", +"duckdb": "2d4b87c5b06ff335bd6c741b", +"postgres": "2d4b87c5b06ff335bd6c741b", +"snowflake": "2d4b87c5b06ff335bd6c741b", +"tsql": "2d4b87c5b06ff335bd6c741b" +} +], +[ +"contracts", +{ +"None": "a2dffd29b4f083e47c096d79", +"bigquery": "a2dffd29b4f083e47c096d79", +"databricks": "a2dffd29b4f083e47c096d79", +"duckdb": "a2dffd29b4f083e47c096d79", +"postgres": "a2dffd29b4f083e47c096d79", +"snowflake": "a2dffd29b4f083e47c096d79", +"tsql": "a2dffd29b4f083e47c096d79" +} +], +[ +"contracts", +{ +"None": "de4c230e461e81a5ec9457cf", +"bigquery": "de4c230e461e81a5ec9457cf", +"databricks": "de4c230e461e81a5ec9457cf", +"duckdb": "de4c230e461e81a5ec9457cf", +"postgres": "de4c230e461e81a5ec9457cf", +"snowflake": "de4c230e461e81a5ec9457cf", +"tsql": "de4c230e461e81a5ec9457cf" +} +], +[ +"contracts", +{ +"None": "ffd2f2034ea38a1c665386fe", +"bigquery": "ffd2f2034ea38a1c665386fe", +"databricks": "ffd2f2034ea38a1c665386fe", +"duckdb": "ffd2f2034ea38a1c665386fe", +"postgres": "ffd2f2034ea38a1c665386fe", +"snowflake": "ffd2f2034ea38a1c665386fe", +"tsql": "ffd2f2034ea38a1c665386fe" +} +], +[ +"contracts", +{ +"None": "b3283bf184bb082f364b8537", +"bigquery": "b3283bf184bb082f364b8537", +"databricks": "b3283bf184bb082f364b8537", +"duckdb": "b3283bf184bb082f364b8537", +"postgres": "b3283bf184bb082f364b8537", +"snowflake": "b3283bf184bb082f364b8537", +"tsql": "b3283bf184bb082f364b8537" +} +], +[ +"contracts", +{ +"None": "c11f8c29184eeda19979e5d2", +"bigquery": "c11f8c29184eeda19979e5d2", +"databricks": "c11f8c29184eeda19979e5d2", +"duckdb": "c11f8c29184eeda19979e5d2", +"postgres": "c11f8c29184eeda19979e5d2", +"snowflake": "c11f8c29184eeda19979e5d2", +"tsql": "c11f8c29184eeda19979e5d2" +} +], +[ +"contracts", +{ +"None": "6a14752b404ff8337dbb4c51", +"bigquery": "6a14752b404ff8337dbb4c51", +"databricks": "6a14752b404ff8337dbb4c51", +"duckdb": "6a14752b404ff8337dbb4c51", +"postgres": "6a14752b404ff8337dbb4c51", +"snowflake": "6a14752b404ff8337dbb4c51", +"tsql": "6a14752b404ff8337dbb4c51" +} +], +[ +"contracts", +{ +"None": "67cc42e5fd32018ec01cf44c", +"bigquery": "67cc42e5fd32018ec01cf44c", +"databricks": "67cc42e5fd32018ec01cf44c", +"duckdb": "67cc42e5fd32018ec01cf44c", +"postgres": "67cc42e5fd32018ec01cf44c", +"snowflake": "67cc42e5fd32018ec01cf44c", +"tsql": "67cc42e5fd32018ec01cf44c" +} +], +[ +"contracts", +{ +"None": "d766e542b46b503b1d64b5ec", +"bigquery": "d766e542b46b503b1d64b5ec", +"databricks": "d766e542b46b503b1d64b5ec", +"duckdb": "d766e542b46b503b1d64b5ec", +"postgres": "d766e542b46b503b1d64b5ec", +"snowflake": "d766e542b46b503b1d64b5ec", +"tsql": "d766e542b46b503b1d64b5ec" +} +], +[ +"contracts", +{ +"None": "f55edb3192180fa6df21c085", +"bigquery": "f55edb3192180fa6df21c085", +"databricks": "f55edb3192180fa6df21c085", +"duckdb": "f55edb3192180fa6df21c085", +"postgres": "f55edb3192180fa6df21c085", +"snowflake": "f55edb3192180fa6df21c085", +"tsql": "f55edb3192180fa6df21c085" +} +], +[ +"contracts", +{ +"None": "b3283bf184bb082f364b8537", +"bigquery": "b3283bf184bb082f364b8537", +"databricks": "b3283bf184bb082f364b8537", +"duckdb": "b3283bf184bb082f364b8537", +"postgres": "b3283bf184bb082f364b8537", +"snowflake": "b3283bf184bb082f364b8537", +"tsql": "b3283bf184bb082f364b8537" +} +], +[ +"contracts", +{ +"None": "36ccf7779e90a958c5dcad96", +"bigquery": "36ccf7779e90a958c5dcad96", +"databricks": "36ccf7779e90a958c5dcad96", +"duckdb": "36ccf7779e90a958c5dcad96", +"postgres": "36ccf7779e90a958c5dcad96", +"snowflake": "36ccf7779e90a958c5dcad96", +"tsql": "36ccf7779e90a958c5dcad96" +} +], +[ +"contracts", +{ +"None": "e44260e0d2411c3cce8781cc", +"bigquery": "e44260e0d2411c3cce8781cc", +"databricks": "e44260e0d2411c3cce8781cc", +"duckdb": "e44260e0d2411c3cce8781cc", +"postgres": "e44260e0d2411c3cce8781cc", +"snowflake": "e44260e0d2411c3cce8781cc", +"tsql": "e44260e0d2411c3cce8781cc" +} +], +[ +"contracts", +{ +"None": "cd5296ac257b6e4f10c3000d", +"bigquery": "cd5296ac257b6e4f10c3000d", +"databricks": "cd5296ac257b6e4f10c3000d", +"duckdb": "cd5296ac257b6e4f10c3000d", +"postgres": "cd5296ac257b6e4f10c3000d", +"snowflake": "cd5296ac257b6e4f10c3000d", +"tsql": "cd5296ac257b6e4f10c3000d" +} +], +[ +"contracts", +{ +"None": "0fb1be349cccda275d77113d", +"bigquery": "0fb1be349cccda275d77113d", +"databricks": "0fb1be349cccda275d77113d", +"duckdb": "0fb1be349cccda275d77113d", +"postgres": "0fb1be349cccda275d77113d", +"snowflake": "0fb1be349cccda275d77113d", +"tsql": "0fb1be349cccda275d77113d" +} +], +[ +"contracts", +{ +"None": "d9843bd5f2e76b8d875badaf", +"bigquery": "d9843bd5f2e76b8d875badaf", +"databricks": "d9843bd5f2e76b8d875badaf", +"duckdb": "d9843bd5f2e76b8d875badaf", +"postgres": "d9843bd5f2e76b8d875badaf", +"snowflake": "d9843bd5f2e76b8d875badaf", +"tsql": "d9843bd5f2e76b8d875badaf" +} +], +[ +"contracts", +{ +"None": "afb26acda81b184518d6d6f5", +"bigquery": "afb26acda81b184518d6d6f5", +"databricks": "f6dafe38c2aa2436ad17d2e6", +"duckdb": "f6dafe38c2aa2436ad17d2e6", +"postgres": "afb26acda81b184518d6d6f5", +"snowflake": "f6dafe38c2aa2436ad17d2e6", +"tsql": "afb26acda81b184518d6d6f5" +} +], +[ +"contracts", +{ +"None": "2a167c9cb4bdcef7cfbcd99a", +"bigquery": "2a167c9cb4bdcef7cfbcd99a", +"databricks": "2a167c9cb4bdcef7cfbcd99a", +"duckdb": "2a167c9cb4bdcef7cfbcd99a", +"postgres": "2a167c9cb4bdcef7cfbcd99a", +"snowflake": "2a167c9cb4bdcef7cfbcd99a", +"tsql": "2a167c9cb4bdcef7cfbcd99a" +} +], +[ +"contracts", +{ +"None": "ea9fe2e44b57cc7f6a5654aa", +"bigquery": "ea9fe2e44b57cc7f6a5654aa", +"databricks": "ea9fe2e44b57cc7f6a5654aa", +"duckdb": "ea9fe2e44b57cc7f6a5654aa", +"postgres": "ea9fe2e44b57cc7f6a5654aa", +"snowflake": "ea9fe2e44b57cc7f6a5654aa", +"tsql": "ea9fe2e44b57cc7f6a5654aa" +} +], +[ +"contracts", +{ +"None": "fe017b6cb02eef5219322f7c", +"bigquery": "fe017b6cb02eef5219322f7c", +"databricks": "fe017b6cb02eef5219322f7c", +"duckdb": "fe017b6cb02eef5219322f7c", +"postgres": "fe017b6cb02eef5219322f7c", +"snowflake": "34168d14eef2cb971b3882fb", +"tsql": "fe017b6cb02eef5219322f7c" +} +], +[ +"contracts", +{ +"None": "7d8609a5dcd3a0f32d347465", +"bigquery": "7d8609a5dcd3a0f32d347465", +"databricks": "7d8609a5dcd3a0f32d347465", +"duckdb": "7d8609a5dcd3a0f32d347465", +"postgres": "7d8609a5dcd3a0f32d347465", +"snowflake": "7d8609a5dcd3a0f32d347465", +"tsql": "7d8609a5dcd3a0f32d347465" +} +], +[ +"contracts", +{ +"None": "8dd6f78d1bbd41f3f2f556fb", +"bigquery": "8dd6f78d1bbd41f3f2f556fb", +"databricks": "8dd6f78d1bbd41f3f2f556fb", +"duckdb": "8dd6f78d1bbd41f3f2f556fb", +"postgres": "8dd6f78d1bbd41f3f2f556fb", +"snowflake": "8dd6f78d1bbd41f3f2f556fb", +"tsql": "8dd6f78d1bbd41f3f2f556fb" +} +], +[ +"contracts", +{ +"None": "891f3d8b2c293698577c7813", +"bigquery": "891f3d8b2c293698577c7813", +"databricks": "891f3d8b2c293698577c7813", +"duckdb": "891f3d8b2c293698577c7813", +"postgres": "891f3d8b2c293698577c7813", +"snowflake": "891f3d8b2c293698577c7813", +"tsql": "891f3d8b2c293698577c7813" +} +], +[ +"contracts", +{ +"None": "cbbf749b3fdb1008cd35c8ee", +"bigquery": "cbbf749b3fdb1008cd35c8ee", +"databricks": "cbbf749b3fdb1008cd35c8ee", +"duckdb": "cbbf749b3fdb1008cd35c8ee", +"postgres": "cbbf749b3fdb1008cd35c8ee", +"snowflake": "cbbf749b3fdb1008cd35c8ee", +"tsql": "cbbf749b3fdb1008cd35c8ee" +} +], +[ +"contracts", +{ +"None": "6f1634dc604b82f58c1d760f", +"bigquery": "6f1634dc604b82f58c1d760f", +"databricks": "6f1634dc604b82f58c1d760f", +"duckdb": "6f1634dc604b82f58c1d760f", +"postgres": "6f1634dc604b82f58c1d760f", +"snowflake": "6f1634dc604b82f58c1d760f", +"tsql": "6f1634dc604b82f58c1d760f" +} +], +[ +"contracts", +{ +"None": "2fb2d4657d6af84c32083288", +"bigquery": "2fb2d4657d6af84c32083288", +"databricks": "2fb2d4657d6af84c32083288", +"duckdb": "2fb2d4657d6af84c32083288", +"postgres": "2fb2d4657d6af84c32083288", +"snowflake": "2fb2d4657d6af84c32083288", +"tsql": "2fb2d4657d6af84c32083288" +} +], +[ +"contracts", +{ +"None": "a9bdcb7ee2642092b9c2e2ae", +"bigquery": "a9bdcb7ee2642092b9c2e2ae", +"databricks": "a9bdcb7ee2642092b9c2e2ae", +"duckdb": "a9bdcb7ee2642092b9c2e2ae", +"postgres": "a9bdcb7ee2642092b9c2e2ae", +"snowflake": "a9bdcb7ee2642092b9c2e2ae", +"tsql": "a9bdcb7ee2642092b9c2e2ae" +} +], +[ +"contracts", +{ +"None": "a8bc34d679ed5c1bd41c17ba", +"bigquery": "0f72c2e3063cc166300becea", +"databricks": "a8bc34d679ed5c1bd41c17ba", +"duckdb": "a8bc34d679ed5c1bd41c17ba", +"postgres": "a8bc34d679ed5c1bd41c17ba", +"snowflake": "0f72c2e3063cc166300becea", +"tsql": "a8bc34d679ed5c1bd41c17ba" +} +], +[ +"contracts", +{ +"None": "6e5f3057275c88687658bb88", +"bigquery": "6e5f3057275c88687658bb88", +"databricks": "6e5f3057275c88687658bb88", +"duckdb": "6e5f3057275c88687658bb88", +"postgres": "6e5f3057275c88687658bb88", +"snowflake": "6e5f3057275c88687658bb88", +"tsql": "6e5f3057275c88687658bb88" +} +], +[ +"contracts", +{ +"None": "b3283bf184bb082f364b8537", +"bigquery": "b3283bf184bb082f364b8537", +"databricks": "b3283bf184bb082f364b8537", +"duckdb": "b3283bf184bb082f364b8537", +"postgres": "b3283bf184bb082f364b8537", +"snowflake": "b3283bf184bb082f364b8537", +"tsql": "b3283bf184bb082f364b8537" +} +], +[ +"contracts", +{ +"None": "6581612a11662f5afd928e61", +"bigquery": "6581612a11662f5afd928e61", +"databricks": "6581612a11662f5afd928e61", +"duckdb": "6581612a11662f5afd928e61", +"postgres": "6581612a11662f5afd928e61", +"snowflake": "6581612a11662f5afd928e61", +"tsql": "6581612a11662f5afd928e61" +} +], +[ +"contracts", +{ +"None": "3405db15b2687320907ae528", +"bigquery": "3405db15b2687320907ae528", +"databricks": "3405db15b2687320907ae528", +"duckdb": "3405db15b2687320907ae528", +"postgres": "3405db15b2687320907ae528", +"snowflake": "3405db15b2687320907ae528", +"tsql": "3405db15b2687320907ae528" +} +], +[ +"contracts", +{ +"None": "1bd8641f841c3847419d163b", +"bigquery": "1bd8641f841c3847419d163b", +"databricks": "1bd8641f841c3847419d163b", +"duckdb": "1bd8641f841c3847419d163b", +"postgres": "1bd8641f841c3847419d163b", +"snowflake": "1bd8641f841c3847419d163b", +"tsql": "1bd8641f841c3847419d163b" +} +], +[ +"contracts", +{ +"None": "b3283bf184bb082f364b8537", +"bigquery": "b3283bf184bb082f364b8537", +"databricks": "b3283bf184bb082f364b8537", +"duckdb": "b3283bf184bb082f364b8537", +"postgres": "b3283bf184bb082f364b8537", +"snowflake": "b3283bf184bb082f364b8537", +"tsql": "b3283bf184bb082f364b8537" +} +], +[ +"contracts", +{ +"None": "70130ae9c4f7aa6a50756c6b", +"bigquery": "70130ae9c4f7aa6a50756c6b", +"databricks": "70130ae9c4f7aa6a50756c6b", +"duckdb": "70130ae9c4f7aa6a50756c6b", +"postgres": "70130ae9c4f7aa6a50756c6b", +"snowflake": "70130ae9c4f7aa6a50756c6b", +"tsql": "70130ae9c4f7aa6a50756c6b" +} +], +[ +"contracts", +{ +"None": "cbbf749b3fdb1008cd35c8ee", +"bigquery": "cbbf749b3fdb1008cd35c8ee", +"databricks": "cbbf749b3fdb1008cd35c8ee", +"duckdb": "cbbf749b3fdb1008cd35c8ee", +"postgres": "cbbf749b3fdb1008cd35c8ee", +"snowflake": "cbbf749b3fdb1008cd35c8ee", +"tsql": "cbbf749b3fdb1008cd35c8ee" +} +], +[ +"contracts", +{ +"None": "0e98df1b9bfd713dda177188", +"bigquery": "0e98df1b9bfd713dda177188", +"databricks": "0e98df1b9bfd713dda177188", +"duckdb": "0e98df1b9bfd713dda177188", +"postgres": "0e98df1b9bfd713dda177188", +"snowflake": "0e98df1b9bfd713dda177188", +"tsql": "0e98df1b9bfd713dda177188" +} +], +[ +"contracts", +{ +"None": "b69ef1eaba1124a7e710d482", +"bigquery": "b69ef1eaba1124a7e710d482", +"databricks": "b69ef1eaba1124a7e710d482", +"duckdb": "b69ef1eaba1124a7e710d482", +"postgres": "b69ef1eaba1124a7e710d482", +"snowflake": "b69ef1eaba1124a7e710d482", +"tsql": "b69ef1eaba1124a7e710d482" +} +], +[ +"contracts", +{ +"None": "866ae81507c3a535c0225de2", +"bigquery": "866ae81507c3a535c0225de2", +"databricks": "866ae81507c3a535c0225de2", +"duckdb": "866ae81507c3a535c0225de2", +"postgres": "866ae81507c3a535c0225de2", +"snowflake": "866ae81507c3a535c0225de2", +"tsql": "866ae81507c3a535c0225de2" +} +] +] diff --git a/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json b/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json new file mode 100644 index 0000000000..1c47a3ac48 --- /dev/null +++ b/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json @@ -0,0 +1,5570 @@ +[ +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "8076db03c4b29af16b33b043", +"81:edges": "7c6782de6171aee87a5c63ca", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "6c2eb1c2dab2a62dcfe944fe", +"81:edges": "59aa1a7bc998bf685d690875", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "8076db03c4b29af16b33b043", +"81:edges": "7c6782de6171aee87a5c63ca", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "6c2eb1c2dab2a62dcfe944fe", +"81:edges": "59aa1a7bc998bf685d690875", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "8076db03c4b29af16b33b043", +"81:edges": "7c6782de6171aee87a5c63ca", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "6c2eb1c2dab2a62dcfe944fe", +"81:edges": "59aa1a7bc998bf685d690875", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "8076db03c4b29af16b33b043", +"81:edges": "7c6782de6171aee87a5c63ca", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "6c2eb1c2dab2a62dcfe944fe", +"81:edges": "59aa1a7bc998bf685d690875", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", +"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "92046d27e611a2b8961bfcc5", +"81:edges": "9c2ff0e1313dd442bb1ac017", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "9cc5472deaed036c6f68e314", +"81:edges": "1960382314765257e21dd758", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", +"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "92046d27e611a2b8961bfcc5", +"81:edges": "9c2ff0e1313dd442bb1ac017", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "9cc5472deaed036c6f68e314", +"81:edges": "1960382314765257e21dd758", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", +"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "92046d27e611a2b8961bfcc5", +"81:edges": "9c2ff0e1313dd442bb1ac017", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "9cc5472deaed036c6f68e314", +"81:edges": "1960382314765257e21dd758", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", +"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", +"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", +"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "92046d27e611a2b8961bfcc5", +"81:edges": "9c2ff0e1313dd442bb1ac017", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "ef2d127de37b942baad06145", +"28:orders_model_9.targeting": "a967e822b57213ec858768ca", +"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", +"36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", +"37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", +"38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", +"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", +"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "9cc5472deaed036c6f68e314", +"81:edges": "1960382314765257e21dd758", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "37d91f3ee68d4dc24cff6ae3", +"81:edges": "13f3bb62cb14932238e532fe", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "2d7de32bd10b12e919c42b4e", +"81:edges": "2309dd07ea524a7832a2e342", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "37d91f3ee68d4dc24cff6ae3", +"81:edges": "13f3bb62cb14932238e532fe", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "2d7de32bd10b12e919c42b4e", +"81:edges": "2309dd07ea524a7832a2e342", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "37d91f3ee68d4dc24cff6ae3", +"81:edges": "13f3bb62cb14932238e532fe", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "2d7de32bd10b12e919c42b4e", +"81:edges": "2309dd07ea524a7832a2e342", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "37d91f3ee68d4dc24cff6ae3", +"81:edges": "13f3bb62cb14932238e532fe", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "2d7de32bd10b12e919c42b4e", +"81:edges": "2309dd07ea524a7832a2e342", +"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", +"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "cd078ceff6e73868a0930f57", +"81:edges": "1e08d370da3b775c6f1f8cec", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "42d3866a260676aa81413170", +"81:edges": "d40d2911f4142b96d5bebfc1", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", +"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "cd078ceff6e73868a0930f57", +"81:edges": "1e08d370da3b775c6f1f8cec", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "42d3866a260676aa81413170", +"81:edges": "d40d2911f4142b96d5bebfc1", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", +"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "cd078ceff6e73868a0930f57", +"81:edges": "1e08d370da3b775c6f1f8cec", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "42d3866a260676aa81413170", +"81:edges": "d40d2911f4142b96d5bebfc1", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", +"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", +"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "cd078ceff6e73868a0930f57", +"81:edges": "1e08d370da3b775c6f1f8cec", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", +"26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", +"27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", +"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", +"31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", +"32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", +"33:orders_model_1.targeting": "6786f18f6ff42aa577c99d86", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", +"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "fcbcf165908dd18a9e49f7ff", +"76:orders_model_8.has_star": "fcbcf165908dd18a9e49f7ff", +"77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", +"78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "42d3866a260676aa81413170", +"81:edges": "d40d2911f4142b96d5bebfc1", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", +"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "766e6cff4a7c97985d321915", +"81:edges": "66def530d7ac79c6a553f347", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", +"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "e3f9826cd489c85cd139787f", +"81:edges": "ff90a6fe0029bd81150fd61c", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", +"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "766e6cff4a7c97985d321915", +"81:edges": "66def530d7ac79c6a553f347", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", +"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "e3f9826cd489c85cd139787f", +"81:edges": "ff90a6fe0029bd81150fd61c", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", +"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "766e6cff4a7c97985d321915", +"81:edges": "66def530d7ac79c6a553f347", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", +"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "e3f9826cd489c85cd139787f", +"81:edges": "ff90a6fe0029bd81150fd61c", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", +"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "766e6cff4a7c97985d321915", +"81:edges": "66def530d7ac79c6a553f347", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", +"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", +"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "e3f9826cd489c85cd139787f", +"81:edges": "ff90a6fe0029bd81150fd61c", +"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", +"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "a14cb265582196761feee603", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "726a3631488829e7d85f22a4", +"81:edges": "353ba5589c448640b32285cf", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", +"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "634a199b0204ba6c3ca102dc", +"81:edges": "5258bb66e465fbd07d87c222", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", +"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "a14cb265582196761feee603", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "726a3631488829e7d85f22a4", +"81:edges": "353ba5589c448640b32285cf", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", +"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "634a199b0204ba6c3ca102dc", +"81:edges": "5258bb66e465fbd07d87c222", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", +"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "a14cb265582196761feee603", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "726a3631488829e7d85f22a4", +"81:edges": "353ba5589c448640b32285cf", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", +"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "634a199b0204ba6c3ca102dc", +"81:edges": "5258bb66e465fbd07d87c222", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", +"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", +"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "a14cb265582196761feee603", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", +"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "726a3631488829e7d85f22a4", +"81:edges": "353ba5589c448640b32285cf", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", +"13:orders_model_15.targeting": "6786f18f6ff42aa577c99d86", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", +"23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", +"24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", +"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", +"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", +"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", +"58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", +"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "634a199b0204ba6c3ca102dc", +"81:edges": "5258bb66e465fbd07d87c222", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "3099535ea109c12832471665", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "51075542f67bd94c29d3f0c4", +"81:edges": "bbe2efc481c2f6f64d4e62a1", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "162bd27b0ba0371264b7f846", +"81:edges": "7f0f5f6e0554b4505652d3aa", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "3099535ea109c12832471665", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "51075542f67bd94c29d3f0c4", +"81:edges": "bbe2efc481c2f6f64d4e62a1", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "162bd27b0ba0371264b7f846", +"81:edges": "7f0f5f6e0554b4505652d3aa", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "3099535ea109c12832471665", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "51075542f67bd94c29d3f0c4", +"81:edges": "bbe2efc481c2f6f64d4e62a1", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "162bd27b0ba0371264b7f846", +"81:edges": "7f0f5f6e0554b4505652d3aa", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "3099535ea109c12832471665", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "51075542f67bd94c29d3f0c4", +"81:edges": "bbe2efc481c2f6f64d4e62a1", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"80:models": "162bd27b0ba0371264b7f846", +"81:edges": "7f0f5f6e0554b4505652d3aa", +"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "27e54dae2504a7f831e0369b", +"81:edges": "3530701087d04534df62ce37", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "582bc1ad59a348db0ee52665", +"81:edges": "d24dafbe5842db03ac5ee2e0", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "27e54dae2504a7f831e0369b", +"81:edges": "3530701087d04534df62ce37", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "582bc1ad59a348db0ee52665", +"81:edges": "d24dafbe5842db03ac5ee2e0", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "27e54dae2504a7f831e0369b", +"81:edges": "3530701087d04534df62ce37", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "582bc1ad59a348db0ee52665", +"81:edges": "d24dafbe5842db03ac5ee2e0", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", +"41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", +"46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", +"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", +"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", +"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", +"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", +"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "27e54dae2504a7f831e0369b", +"81:edges": "3530701087d04534df62ce37", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +], +[ +"lineage", +{ +"0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", +"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", +"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", +"21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", +"22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", +"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", +"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", +"26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", +"27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", +"31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", +"35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", +"36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", +"41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", +"42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", +"43:orders_model_13.targeting": "6786f18f6ff42aa577c99d86", +"44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", +"45:orders_model_4.has_model": "fcbcf165908dd18a9e49f7ff", +"46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", +"47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", +"48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", +"49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", +"51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", +"52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", +"53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", +"54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", +"55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", +"56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", +"57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", +"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"59:orders_model_11.sourced": "623473241d8ca9bda2996980", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", +"61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", +"62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", +"63:orders_model_14.targeting": "6786f18f6ff42aa577c99d86", +"64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", +"65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", +"66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", +"67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", +"68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", +"69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", +"71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", +"72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", +"73:orders_model_5.targeting": "6786f18f6ff42aa577c99d86", +"74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", +"75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", +"76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", +"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", +"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"80:models": "582bc1ad59a348db0ee52665", +"81:edges": "d24dafbe5842db03ac5ee2e0", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +} +] +] diff --git a/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json b/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json new file mode 100644 index 0000000000..a7ccced078 --- /dev/null +++ b/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json @@ -0,0 +1,25502 @@ +[ +[ +"type", +{ +"DuckDB": "7e163d1c3f6840b5164def94", +"None": "7e163d1c3f6840b5164def94", +"bigquery": "7e163d1c3f6840b5164def94", +"databricks": "7e163d1c3f6840b5164def94", +"duckdb": "7e163d1c3f6840b5164def94", +"generic": "7e163d1c3f6840b5164def94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7e163d1c3f6840b5164def94", +"postgres": "7e163d1c3f6840b5164def94", +"postgresql": "7e163d1c3f6840b5164def94", +"snowflake": "7e163d1c3f6840b5164def94", +"tsql": "7e163d1c3f6840b5164def94" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "e6badc927d1adf4563b4f3a5", +"None": "e6badc927d1adf4563b4f3a5", +"bigquery": "e6badc927d1adf4563b4f3a5", +"databricks": "e6badc927d1adf4563b4f3a5", +"duckdb": "e6badc927d1adf4563b4f3a5", +"generic": "e6badc927d1adf4563b4f3a5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e6badc927d1adf4563b4f3a5", +"postgres": "e6badc927d1adf4563b4f3a5", +"postgresql": "e6badc927d1adf4563b4f3a5", +"snowflake": "e6badc927d1adf4563b4f3a5", +"tsql": "e6badc927d1adf4563b4f3a5" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "bbb2563edda71d0b69ef0855", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "bbb2563edda71d0b69ef0855", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "bbb2563edda71d0b69ef0855", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "51d717b08676179ee1ccefe4", +"postgres": "bbb2563edda71d0b69ef0855", +"postgresql": "bbb2563edda71d0b69ef0855", +"snowflake": "bbb2563edda71d0b69ef0855", +"tsql": "83fd3ed7282be0616dc3037e" +} +], +[ +"type", +{ +"DuckDB": "688179272da645629077e980", +"None": "688179272da645629077e980", +"bigquery": "688179272da645629077e980", +"databricks": "688179272da645629077e980", +"duckdb": "688179272da645629077e980", +"generic": "688179272da645629077e980", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "688179272da645629077e980", +"postgres": "688179272da645629077e980", +"postgresql": "688179272da645629077e980", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "688179272da645629077e980" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "5c8b98f516cedefc237fa721", +"None": "5c8b98f516cedefc237fa721", +"bigquery": "5c8b98f516cedefc237fa721", +"databricks": "5c8b98f516cedefc237fa721", +"duckdb": "5c8b98f516cedefc237fa721", +"generic": "5c8b98f516cedefc237fa721", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c8b98f516cedefc237fa721", +"postgres": "5c8b98f516cedefc237fa721", +"postgresql": "5c8b98f516cedefc237fa721", +"snowflake": "5c8b98f516cedefc237fa721", +"tsql": "5c8b98f516cedefc237fa721" +} +], +[ +"type", +{ +"DuckDB": "d00038134428e2ce04bc8e09", +"None": "ab7940999dc6f4c634249230", +"bigquery": "ab7940999dc6f4c634249230", +"databricks": "ab7940999dc6f4c634249230", +"duckdb": "d00038134428e2ce04bc8e09", +"generic": "ab7940999dc6f4c634249230", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab7940999dc6f4c634249230", +"postgres": "ab7940999dc6f4c634249230", +"postgresql": "ab7940999dc6f4c634249230", +"snowflake": "d00038134428e2ce04bc8e09", +"tsql": "ab7940999dc6f4c634249230" +} +], +[ +"type", +{ +"DuckDB": "ee1b4ffc65518c536e3632ee", +"None": "ee1b4ffc65518c536e3632ee", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "ee1b4ffc65518c536e3632ee", +"duckdb": "ee1b4ffc65518c536e3632ee", +"generic": "ee1b4ffc65518c536e3632ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee1b4ffc65518c536e3632ee", +"postgres": "ee1b4ffc65518c536e3632ee", +"postgresql": "ee1b4ffc65518c536e3632ee", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "ee1b4ffc65518c536e3632ee" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "0f799a95a4f5ef502897f02c", +"None": "0f799a95a4f5ef502897f02c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "0f799a95a4f5ef502897f02c", +"duckdb": "0f799a95a4f5ef502897f02c", +"generic": "0f799a95a4f5ef502897f02c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0f799a95a4f5ef502897f02c", +"postgres": "0f799a95a4f5ef502897f02c", +"postgresql": "0f799a95a4f5ef502897f02c", +"snowflake": "0f799a95a4f5ef502897f02c", +"tsql": "0f799a95a4f5ef502897f02c" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "19bdd92ea0cc5fb028657510", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "19bdd92ea0cc5fb028657510", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "19bdd92ea0cc5fb028657510", +"postgresql": "19bdd92ea0cc5fb028657510", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "901e8c830028411846541f65" +} +], +[ +"type", +{ +"DuckDB": "e1c36a5cdd2427ff2b089ac9", +"None": "e1c36a5cdd2427ff2b089ac9", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "e1c36a5cdd2427ff2b089ac9", +"duckdb": "e1c36a5cdd2427ff2b089ac9", +"generic": "e1c36a5cdd2427ff2b089ac9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c36a5cdd2427ff2b089ac9", +"postgres": "e1c36a5cdd2427ff2b089ac9", +"postgresql": "e1c36a5cdd2427ff2b089ac9", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "e1c36a5cdd2427ff2b089ac9" +} +], +[ +"type", +{ +"DuckDB": "73151c1398b30e5c5d18f4ce", +"None": "73151c1398b30e5c5d18f4ce", +"bigquery": "73151c1398b30e5c5d18f4ce", +"databricks": "73151c1398b30e5c5d18f4ce", +"duckdb": "73151c1398b30e5c5d18f4ce", +"generic": "73151c1398b30e5c5d18f4ce", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73151c1398b30e5c5d18f4ce", +"postgres": "73151c1398b30e5c5d18f4ce", +"postgresql": "73151c1398b30e5c5d18f4ce", +"snowflake": "73151c1398b30e5c5d18f4ce", +"tsql": "73151c1398b30e5c5d18f4ce" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "78b2cf275c640af54feeca31", +"None": "78b2cf275c640af54feeca31", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "78b2cf275c640af54feeca31", +"duckdb": "78b2cf275c640af54feeca31", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "78b2cf275c640af54feeca31", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "f15c56475abfc73488abe63b", +"None": "f15c56475abfc73488abe63b", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f15c56475abfc73488abe63b", +"duckdb": "f15c56475abfc73488abe63b", +"generic": "f15c56475abfc73488abe63b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f15c56475abfc73488abe63b", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f15c56475abfc73488abe63b" +} +], +[ +"type", +{ +"DuckDB": "717cf79ef777823eb3576deb", +"None": "717cf79ef777823eb3576deb", +"bigquery": "717cf79ef777823eb3576deb", +"databricks": "717cf79ef777823eb3576deb", +"duckdb": "717cf79ef777823eb3576deb", +"generic": "717cf79ef777823eb3576deb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "717cf79ef777823eb3576deb", +"postgres": "717cf79ef777823eb3576deb", +"postgresql": "717cf79ef777823eb3576deb", +"snowflake": "717cf79ef777823eb3576deb", +"tsql": "717cf79ef777823eb3576deb" +} +], +[ +"type", +{ +"DuckDB": "a6c6f4aa4257d422cbf3f875", +"None": "a6c6f4aa4257d422cbf3f875", +"bigquery": "ea191a784359be345cd9ded6", +"databricks": "ea191a784359be345cd9ded6", +"duckdb": "a6c6f4aa4257d422cbf3f875", +"generic": "a6c6f4aa4257d422cbf3f875", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ea191a784359be345cd9ded6", +"postgres": "a6c6f4aa4257d422cbf3f875", +"postgresql": "a6c6f4aa4257d422cbf3f875", +"snowflake": "a6c6f4aa4257d422cbf3f875", +"tsql": "a6c6f4aa4257d422cbf3f875" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "78b2cf275c640af54feeca31", +"bigquery": "78b2cf275c640af54feeca31", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "0f799a95a4f5ef502897f02c", +"None": "0f799a95a4f5ef502897f02c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "0f799a95a4f5ef502897f02c", +"duckdb": "0f799a95a4f5ef502897f02c", +"generic": "0f799a95a4f5ef502897f02c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0f799a95a4f5ef502897f02c", +"postgres": "0f799a95a4f5ef502897f02c", +"postgresql": "0f799a95a4f5ef502897f02c", +"snowflake": "0f799a95a4f5ef502897f02c", +"tsql": "0f799a95a4f5ef502897f02c" +} +], +[ +"type", +{ +"DuckDB": "e9b32e3b5a5505b32f33e169", +"None": "e9b32e3b5a5505b32f33e169", +"bigquery": "e9b32e3b5a5505b32f33e169", +"databricks": "e9b32e3b5a5505b32f33e169", +"duckdb": "e9b32e3b5a5505b32f33e169", +"generic": "e9b32e3b5a5505b32f33e169", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e9b32e3b5a5505b32f33e169", +"postgres": "e9b32e3b5a5505b32f33e169", +"postgresql": "e9b32e3b5a5505b32f33e169", +"snowflake": "e9b32e3b5a5505b32f33e169", +"tsql": "e9b32e3b5a5505b32f33e169" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "a766fea06fc070b352976b0a", +"None": "a766fea06fc070b352976b0a", +"bigquery": "e4f0244beaa01667e80482b6", +"databricks": "e4f0244beaa01667e80482b6", +"duckdb": "a766fea06fc070b352976b0a", +"generic": "a766fea06fc070b352976b0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e4f0244beaa01667e80482b6", +"postgres": "a766fea06fc070b352976b0a", +"postgresql": "a766fea06fc070b352976b0a", +"snowflake": "a766fea06fc070b352976b0a", +"tsql": "a766fea06fc070b352976b0a" +} +], +[ +"type", +{ +"DuckDB": "4114b18e7e26d16613331fa7", +"None": "4114b18e7e26d16613331fa7", +"bigquery": "4114b18e7e26d16613331fa7", +"databricks": "4fb572024031cb7631777be1", +"duckdb": "4114b18e7e26d16613331fa7", +"generic": "4114b18e7e26d16613331fa7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4114b18e7e26d16613331fa7", +"postgres": "4114b18e7e26d16613331fa7", +"postgresql": "4114b18e7e26d16613331fa7", +"snowflake": "4114b18e7e26d16613331fa7", +"tsql": "4114b18e7e26d16613331fa7" +} +], +[ +"type", +{ +"DuckDB": "98a80237e4e0a12f35bbf17e", +"None": "98a80237e4e0a12f35bbf17e", +"bigquery": "98a80237e4e0a12f35bbf17e", +"databricks": "98a80237e4e0a12f35bbf17e", +"duckdb": "98a80237e4e0a12f35bbf17e", +"generic": "98a80237e4e0a12f35bbf17e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "98a80237e4e0a12f35bbf17e", +"postgres": "98a80237e4e0a12f35bbf17e", +"postgresql": "98a80237e4e0a12f35bbf17e", +"snowflake": "98a80237e4e0a12f35bbf17e", +"tsql": "98a80237e4e0a12f35bbf17e" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "9a79cb58680bebe49009a72d", +"None": "9a79cb58680bebe49009a72d", +"bigquery": "9a79cb58680bebe49009a72d", +"databricks": "9a79cb58680bebe49009a72d", +"duckdb": "9a79cb58680bebe49009a72d", +"generic": "9a79cb58680bebe49009a72d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9a79cb58680bebe49009a72d", +"postgres": "9a79cb58680bebe49009a72d", +"postgresql": "9a79cb58680bebe49009a72d", +"snowflake": "9a79cb58680bebe49009a72d", +"tsql": "9a79cb58680bebe49009a72d" +} +], +[ +"type", +{ +"DuckDB": "9bafb08d41fd1c208b5deca4", +"None": "9bafb08d41fd1c208b5deca4", +"bigquery": "9bafb08d41fd1c208b5deca4", +"databricks": "9bafb08d41fd1c208b5deca4", +"duckdb": "9bafb08d41fd1c208b5deca4", +"generic": "9bafb08d41fd1c208b5deca4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9bafb08d41fd1c208b5deca4", +"postgres": "9bafb08d41fd1c208b5deca4", +"postgresql": "9bafb08d41fd1c208b5deca4", +"snowflake": "9bafb08d41fd1c208b5deca4", +"tsql": "9bafb08d41fd1c208b5deca4" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "2e60720c3255d112fcd0938a", +"None": "2e60720c3255d112fcd0938a", +"bigquery": "2e60720c3255d112fcd0938a", +"databricks": "2e60720c3255d112fcd0938a", +"duckdb": "2e60720c3255d112fcd0938a", +"generic": "2e60720c3255d112fcd0938a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2e60720c3255d112fcd0938a", +"postgres": "2e60720c3255d112fcd0938a", +"postgresql": "2e60720c3255d112fcd0938a", +"snowflake": "2e60720c3255d112fcd0938a", +"tsql": "2e60720c3255d112fcd0938a" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "c9cc0c1ce817773e19f141b5", +"None": "d7249dcf128bf2bb675aa0d9", +"bigquery": "d7249dcf128bf2bb675aa0d9", +"databricks": "f1640dc6a7437eae3cc39eec", +"duckdb": "c9cc0c1ce817773e19f141b5", +"generic": "d7249dcf128bf2bb675aa0d9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d7249dcf128bf2bb675aa0d9", +"postgres": "d7249dcf128bf2bb675aa0d9", +"postgresql": "d7249dcf128bf2bb675aa0d9", +"snowflake": "89091c4ddfa26794a7c15ab6", +"tsql": "d7249dcf128bf2bb675aa0d9" +} +], +[ +"type", +{ +"DuckDB": "e060b5a5a8e9774b580d58d3", +"None": "e060b5a5a8e9774b580d58d3", +"bigquery": "319a9fbb02ed503eb114164b", +"databricks": "319a9fbb02ed503eb114164b", +"duckdb": "e060b5a5a8e9774b580d58d3", +"generic": "e060b5a5a8e9774b580d58d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "319a9fbb02ed503eb114164b", +"postgres": "e060b5a5a8e9774b580d58d3", +"postgresql": "e060b5a5a8e9774b580d58d3", +"snowflake": "e060b5a5a8e9774b580d58d3", +"tsql": "e060b5a5a8e9774b580d58d3" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "8111d803c9182d0189c728e9", +"None": "8111d803c9182d0189c728e9", +"bigquery": "8111d803c9182d0189c728e9", +"databricks": "8111d803c9182d0189c728e9", +"duckdb": "8111d803c9182d0189c728e9", +"generic": "8111d803c9182d0189c728e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8111d803c9182d0189c728e9", +"postgres": "8111d803c9182d0189c728e9", +"postgresql": "8111d803c9182d0189c728e9", +"snowflake": "8111d803c9182d0189c728e9", +"tsql": "8111d803c9182d0189c728e9" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "d79c3ac70093cb4cb531e71c", +"None": "d79c3ac70093cb4cb531e71c", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "d79c3ac70093cb4cb531e71c", +"duckdb": "d79c3ac70093cb4cb531e71c", +"generic": "d79c3ac70093cb4cb531e71c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d79c3ac70093cb4cb531e71c", +"postgres": "d79c3ac70093cb4cb531e71c", +"postgresql": "d79c3ac70093cb4cb531e71c", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d79c3ac70093cb4cb531e71c" +} +], +[ +"type", +{ +"DuckDB": "4b356542044f1267fcfc487a", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "4b356542044f1267fcfc487a", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "f0ec29663977eb66dca07a0e", +"None": "f0ec29663977eb66dca07a0e", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "f0ec29663977eb66dca07a0e", +"duckdb": "f0ec29663977eb66dca07a0e", +"generic": "f0ec29663977eb66dca07a0e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f0ec29663977eb66dca07a0e", +"postgres": "f0ec29663977eb66dca07a0e", +"postgresql": "f0ec29663977eb66dca07a0e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f0ec29663977eb66dca07a0e" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "64fc778308b798a730b1d5c9", +"None": "64fc778308b798a730b1d5c9", +"bigquery": "64fc778308b798a730b1d5c9", +"databricks": "64fc778308b798a730b1d5c9", +"duckdb": "64fc778308b798a730b1d5c9", +"generic": "64fc778308b798a730b1d5c9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "64fc778308b798a730b1d5c9", +"postgres": "64fc778308b798a730b1d5c9", +"postgresql": "64fc778308b798a730b1d5c9", +"snowflake": "64fc778308b798a730b1d5c9", +"tsql": "64fc778308b798a730b1d5c9" +} +], +[ +"type", +{ +"DuckDB": "b46dfa976d5ba04f95bea32b", +"None": "b46dfa976d5ba04f95bea32b", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "b46dfa976d5ba04f95bea32b", +"duckdb": "b46dfa976d5ba04f95bea32b", +"generic": "b46dfa976d5ba04f95bea32b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b46dfa976d5ba04f95bea32b", +"postgres": "34dcdfe3e3afe6140e1353e0", +"postgresql": "34dcdfe3e3afe6140e1353e0", +"snowflake": "b46dfa976d5ba04f95bea32b", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "20c9f38cf0da7cc6079a3a47", +"None": "20c9f38cf0da7cc6079a3a47", +"bigquery": "20c9f38cf0da7cc6079a3a47", +"databricks": "20c9f38cf0da7cc6079a3a47", +"duckdb": "20c9f38cf0da7cc6079a3a47", +"generic": "20c9f38cf0da7cc6079a3a47", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "20c9f38cf0da7cc6079a3a47", +"postgres": "20c9f38cf0da7cc6079a3a47", +"postgresql": "20c9f38cf0da7cc6079a3a47", +"snowflake": "20c9f38cf0da7cc6079a3a47", +"tsql": "20c9f38cf0da7cc6079a3a47" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "09a5861e271d932cd3a9a264", +"None": "6ef74921b43664a98de3f7e4", +"bigquery": "47e0816fecdd9e015d7aeb79", +"databricks": "6ef74921b43664a98de3f7e4", +"duckdb": "09a5861e271d932cd3a9a264", +"generic": "6ef74921b43664a98de3f7e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "cc685e113aac64bd76fbc3cb", +"postgresql": "cc685e113aac64bd76fbc3cb", +"snowflake": "6fe4c4c52324360189048430", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "35e216b6921bf843994f2eea", +"None": "46ec3e19e05a6cb29854ccac", +"bigquery": "46ec3e19e05a6cb29854ccac", +"databricks": "46ec3e19e05a6cb29854ccac", +"duckdb": "35e216b6921bf843994f2eea", +"generic": "46ec3e19e05a6cb29854ccac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "46ec3e19e05a6cb29854ccac", +"postgres": "46ec3e19e05a6cb29854ccac", +"postgresql": "46ec3e19e05a6cb29854ccac", +"snowflake": "46ec3e19e05a6cb29854ccac", +"tsql": "46ec3e19e05a6cb29854ccac" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "1950e32420d1d40d41ffae64", +"None": "1950e32420d1d40d41ffae64", +"bigquery": "1950e32420d1d40d41ffae64", +"databricks": "1950e32420d1d40d41ffae64", +"duckdb": "1950e32420d1d40d41ffae64", +"generic": "1950e32420d1d40d41ffae64", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1950e32420d1d40d41ffae64", +"postgres": "1950e32420d1d40d41ffae64", +"postgresql": "1950e32420d1d40d41ffae64", +"snowflake": "1950e32420d1d40d41ffae64", +"tsql": "1950e32420d1d40d41ffae64" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "9e4a50a8aab648a9737f7d0c", +"None": "9e4a50a8aab648a9737f7d0c", +"bigquery": "d9a4e245156e760303573500", +"databricks": "9e4a50a8aab648a9737f7d0c", +"duckdb": "9e4a50a8aab648a9737f7d0c", +"generic": "9e4a50a8aab648a9737f7d0c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9e4a50a8aab648a9737f7d0c", +"postgres": "36d29258901a4d8c296baa68", +"postgresql": "36d29258901a4d8c296baa68", +"snowflake": "9e4a50a8aab648a9737f7d0c", +"tsql": "355e032cc5c0f24695836b3d" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "a7120d1b1abcb844c87a968a", +"None": "a7120d1b1abcb844c87a968a", +"bigquery": "a7120d1b1abcb844c87a968a", +"databricks": "a7120d1b1abcb844c87a968a", +"duckdb": "a7120d1b1abcb844c87a968a", +"generic": "a7120d1b1abcb844c87a968a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a7120d1b1abcb844c87a968a", +"postgres": "a7120d1b1abcb844c87a968a", +"postgresql": "a7120d1b1abcb844c87a968a", +"snowflake": "a7120d1b1abcb844c87a968a", +"tsql": "a7120d1b1abcb844c87a968a" +} +], +[ +"type", +{ +"DuckDB": "cc2a8dbf7a9806988a7b5067", +"None": "cc2a8dbf7a9806988a7b5067", +"bigquery": "cc2a8dbf7a9806988a7b5067", +"databricks": "cc2a8dbf7a9806988a7b5067", +"duckdb": "cc2a8dbf7a9806988a7b5067", +"generic": "cc2a8dbf7a9806988a7b5067", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cc2a8dbf7a9806988a7b5067", +"postgres": "cc2a8dbf7a9806988a7b5067", +"postgresql": "cc2a8dbf7a9806988a7b5067", +"snowflake": "cc2a8dbf7a9806988a7b5067", +"tsql": "cc2a8dbf7a9806988a7b5067" +} +], +[ +"type", +{ +"DuckDB": "5d4d5145235f17764810d03f", +"None": "5d4d5145235f17764810d03f", +"bigquery": "5d4d5145235f17764810d03f", +"databricks": "5d4d5145235f17764810d03f", +"duckdb": "5d4d5145235f17764810d03f", +"generic": "5d4d5145235f17764810d03f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5d4d5145235f17764810d03f", +"postgres": "5d4d5145235f17764810d03f", +"postgresql": "5d4d5145235f17764810d03f", +"snowflake": "5d4d5145235f17764810d03f", +"tsql": "5d4d5145235f17764810d03f" +} +], +[ +"type", +{ +"DuckDB": "2427f5522575cdc3d6a74e4c", +"None": "2427f5522575cdc3d6a74e4c", +"bigquery": "2427f5522575cdc3d6a74e4c", +"databricks": "2427f5522575cdc3d6a74e4c", +"duckdb": "2427f5522575cdc3d6a74e4c", +"generic": "2427f5522575cdc3d6a74e4c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2427f5522575cdc3d6a74e4c", +"postgres": "2427f5522575cdc3d6a74e4c", +"postgresql": "2427f5522575cdc3d6a74e4c", +"snowflake": "2427f5522575cdc3d6a74e4c", +"tsql": "2427f5522575cdc3d6a74e4c" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "97d3bfe2b726a642c0011696", +"None": "5a48ec343f2513c62588cb55", +"bigquery": "5a48ec343f2513c62588cb55", +"databricks": "12b8a08a67c6c5ca887183e3", +"duckdb": "97d3bfe2b726a642c0011696", +"generic": "5a48ec343f2513c62588cb55", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5a48ec343f2513c62588cb55", +"postgres": "5a48ec343f2513c62588cb55", +"postgresql": "5a48ec343f2513c62588cb55", +"snowflake": "582c76388cd6468a1e793692", +"tsql": "5a48ec343f2513c62588cb55" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "b5c4d5044b2a6e70633f0d0a", +"None": "b5c4d5044b2a6e70633f0d0a", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "b5c4d5044b2a6e70633f0d0a", +"duckdb": "b5c4d5044b2a6e70633f0d0a", +"generic": "b5c4d5044b2a6e70633f0d0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b5c4d5044b2a6e70633f0d0a", +"postgres": "b5c4d5044b2a6e70633f0d0a", +"postgresql": "b5c4d5044b2a6e70633f0d0a", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "b5c4d5044b2a6e70633f0d0a" +} +], +[ +"type", +{ +"DuckDB": "7a8df655ee308ea4f929b9fd", +"None": "7a8df655ee308ea4f929b9fd", +"bigquery": "ac984ee30077ed3bb3c7c9d5", +"databricks": "ac984ee30077ed3bb3c7c9d5", +"duckdb": "7a8df655ee308ea4f929b9fd", +"generic": "7a8df655ee308ea4f929b9fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ac984ee30077ed3bb3c7c9d5", +"postgres": "7a8df655ee308ea4f929b9fd", +"postgresql": "7a8df655ee308ea4f929b9fd", +"snowflake": "7a8df655ee308ea4f929b9fd", +"tsql": "7a8df655ee308ea4f929b9fd" +} +], +[ +"type", +{ +"DuckDB": "804fec6876d0b43935790142", +"None": "804fec6876d0b43935790142", +"bigquery": "804fec6876d0b43935790142", +"databricks": "804fec6876d0b43935790142", +"duckdb": "804fec6876d0b43935790142", +"generic": "804fec6876d0b43935790142", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "804fec6876d0b43935790142", +"postgres": "804fec6876d0b43935790142", +"postgresql": "804fec6876d0b43935790142", +"snowflake": "804fec6876d0b43935790142", +"tsql": "804fec6876d0b43935790142" +} +], +[ +"type", +{ +"DuckDB": "8a4c61c12f9eaa564d1c19a1", +"None": "8a4c61c12f9eaa564d1c19a1", +"bigquery": "8a4c61c12f9eaa564d1c19a1", +"databricks": "8a4c61c12f9eaa564d1c19a1", +"duckdb": "8a4c61c12f9eaa564d1c19a1", +"generic": "8a4c61c12f9eaa564d1c19a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8a4c61c12f9eaa564d1c19a1", +"postgres": "8a4c61c12f9eaa564d1c19a1", +"postgresql": "8a4c61c12f9eaa564d1c19a1", +"snowflake": "8a4c61c12f9eaa564d1c19a1", +"tsql": "8a4c61c12f9eaa564d1c19a1" +} +], +[ +"type", +{ +"DuckDB": "fcb2bd160300a30539bd5398", +"None": "fcb2bd160300a30539bd5398", +"bigquery": "fcb2bd160300a30539bd5398", +"databricks": "fcb2bd160300a30539bd5398", +"duckdb": "fcb2bd160300a30539bd5398", +"generic": "fcb2bd160300a30539bd5398", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fcb2bd160300a30539bd5398", +"postgres": "fcb2bd160300a30539bd5398", +"postgresql": "fcb2bd160300a30539bd5398", +"snowflake": "fcb2bd160300a30539bd5398", +"tsql": "fcb2bd160300a30539bd5398" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "c0eec31620dfc2ac0028173d", +"None": "d3bcf10e00645b12a802c7d0", +"bigquery": "02e0b2ed25f32a8d09350a23", +"databricks": "d3bcf10e00645b12a802c7d0", +"duckdb": "c0eec31620dfc2ac0028173d", +"generic": "d3bcf10e00645b12a802c7d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "c0eec31620dfc2ac0028173d", +"postgresql": "c0eec31620dfc2ac0028173d", +"snowflake": "d379c0ee4dad89e570b7329e", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "06aec7cca4b30489405665e2", +"None": "06aec7cca4b30489405665e2", +"bigquery": "06aec7cca4b30489405665e2", +"databricks": "06aec7cca4b30489405665e2", +"duckdb": "06aec7cca4b30489405665e2", +"generic": "06aec7cca4b30489405665e2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "06aec7cca4b30489405665e2", +"postgres": "06aec7cca4b30489405665e2", +"postgresql": "06aec7cca4b30489405665e2", +"snowflake": "06aec7cca4b30489405665e2", +"tsql": "06aec7cca4b30489405665e2" +} +], +[ +"type", +{ +"DuckDB": "76b7de42fb46036e5c9279bf", +"None": "76b7de42fb46036e5c9279bf", +"bigquery": "76b7de42fb46036e5c9279bf", +"databricks": "76b7de42fb46036e5c9279bf", +"duckdb": "76b7de42fb46036e5c9279bf", +"generic": "76b7de42fb46036e5c9279bf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "76b7de42fb46036e5c9279bf", +"postgres": "76b7de42fb46036e5c9279bf", +"postgresql": "76b7de42fb46036e5c9279bf", +"snowflake": "76b7de42fb46036e5c9279bf", +"tsql": "76b7de42fb46036e5c9279bf" +} +], +[ +"type", +{ +"DuckDB": "93c1b5b5960e26479baf88d0", +"None": "93c1b5b5960e26479baf88d0", +"bigquery": "93c1b5b5960e26479baf88d0", +"databricks": "93c1b5b5960e26479baf88d0", +"duckdb": "93c1b5b5960e26479baf88d0", +"generic": "93c1b5b5960e26479baf88d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "93c1b5b5960e26479baf88d0", +"postgres": "93c1b5b5960e26479baf88d0", +"postgresql": "93c1b5b5960e26479baf88d0", +"snowflake": "93c1b5b5960e26479baf88d0", +"tsql": "93c1b5b5960e26479baf88d0" +} +], +[ +"type", +{ +"DuckDB": "3b3de43279bf3f0706595e4a", +"None": "9db9d9c0f19fb71af06e1044", +"bigquery": "a4d165ed06d0b1b815192ef4", +"databricks": "9db9d9c0f19fb71af06e1044", +"duckdb": "3b3de43279bf3f0706595e4a", +"generic": "9db9d9c0f19fb71af06e1044", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "3b3de43279bf3f0706595e4a", +"postgresql": "3b3de43279bf3f0706595e4a", +"snowflake": "0b6d401f07331fac8353bbd3", +"tsql": "b9c379a586b78ced66993a52" +} +], +[ +"type", +{ +"DuckDB": "6965b3d5426a0af9750f9a03", +"None": "6965b3d5426a0af9750f9a03", +"bigquery": "6965b3d5426a0af9750f9a03", +"databricks": "6965b3d5426a0af9750f9a03", +"duckdb": "6965b3d5426a0af9750f9a03", +"generic": "6965b3d5426a0af9750f9a03", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6965b3d5426a0af9750f9a03", +"postgres": "6965b3d5426a0af9750f9a03", +"postgresql": "6965b3d5426a0af9750f9a03", +"snowflake": "6965b3d5426a0af9750f9a03", +"tsql": "6965b3d5426a0af9750f9a03" +} +], +[ +"type", +{ +"DuckDB": "7aa59eed1bba74a9dee48e59", +"None": "7aa59eed1bba74a9dee48e59", +"bigquery": "7aa59eed1bba74a9dee48e59", +"databricks": "7aa59eed1bba74a9dee48e59", +"duckdb": "7aa59eed1bba74a9dee48e59", +"generic": "7aa59eed1bba74a9dee48e59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7aa59eed1bba74a9dee48e59", +"postgres": "7aa59eed1bba74a9dee48e59", +"postgresql": "7aa59eed1bba74a9dee48e59", +"snowflake": "7aa59eed1bba74a9dee48e59", +"tsql": "7aa59eed1bba74a9dee48e59" +} +], +[ +"type", +{ +"DuckDB": "66e7166890ccedeaad6c241a", +"None": "66e7166890ccedeaad6c241a", +"bigquery": "66e7166890ccedeaad6c241a", +"databricks": "66e7166890ccedeaad6c241a", +"duckdb": "66e7166890ccedeaad6c241a", +"generic": "66e7166890ccedeaad6c241a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "66e7166890ccedeaad6c241a", +"postgres": "66e7166890ccedeaad6c241a", +"postgresql": "66e7166890ccedeaad6c241a", +"snowflake": "66e7166890ccedeaad6c241a", +"tsql": "66e7166890ccedeaad6c241a" +} +], +[ +"type", +{ +"DuckDB": "9aff7fcc7d550e1a8d105471", +"None": "9aff7fcc7d550e1a8d105471", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "9aff7fcc7d550e1a8d105471", +"duckdb": "9aff7fcc7d550e1a8d105471", +"generic": "9aff7fcc7d550e1a8d105471", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9aff7fcc7d550e1a8d105471", +"postgres": "9aff7fcc7d550e1a8d105471", +"postgresql": "9aff7fcc7d550e1a8d105471", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "9aff7fcc7d550e1a8d105471" +} +], +[ +"type", +{ +"DuckDB": "b561884132dbe7e42b719110", +"None": "b561884132dbe7e42b719110", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "b561884132dbe7e42b719110", +"duckdb": "b561884132dbe7e42b719110", +"generic": "b561884132dbe7e42b719110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b561884132dbe7e42b719110", +"postgres": "b561884132dbe7e42b719110", +"postgresql": "b561884132dbe7e42b719110", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "b561884132dbe7e42b719110" +} +], +[ +"type", +{ +"DuckDB": "fb6eefdc9bed918e3ac82197", +"None": "fb6eefdc9bed918e3ac82197", +"bigquery": "fb6eefdc9bed918e3ac82197", +"databricks": "fb6eefdc9bed918e3ac82197", +"duckdb": "fb6eefdc9bed918e3ac82197", +"generic": "fb6eefdc9bed918e3ac82197", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fb6eefdc9bed918e3ac82197", +"postgres": "fb6eefdc9bed918e3ac82197", +"postgresql": "fb6eefdc9bed918e3ac82197", +"snowflake": "fb6eefdc9bed918e3ac82197", +"tsql": "fb6eefdc9bed918e3ac82197" +} +], +[ +"type", +{ +"DuckDB": "8698d724534891869075175e", +"None": "0017a88bb60ec3403c786ed8", +"bigquery": "47e0816fecdd9e015d7aeb79", +"databricks": "0017a88bb60ec3403c786ed8", +"duckdb": "8698d724534891869075175e", +"generic": "0017a88bb60ec3403c786ed8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8698d724534891869075175e", +"postgresql": "8698d724534891869075175e", +"snowflake": "e6f36041b1d9fb8fe3a18cbb", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "ee07062f9d57cfdfb20f327b", +"None": "ee07062f9d57cfdfb20f327b", +"bigquery": "1b5f2388e58e5d99180f6450", +"databricks": "0a22fc4bfa30bc1953d41de6", +"duckdb": "ee07062f9d57cfdfb20f327b", +"generic": "ee07062f9d57cfdfb20f327b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee07062f9d57cfdfb20f327b", +"postgres": "0a9a262e0d175ab0eb0865e2", +"postgresql": "0a9a262e0d175ab0eb0865e2", +"snowflake": "0f4d95882eb1debabb5a887b", +"tsql": "ee374268d4ffdfcf4f1eec33" +} +], +[ +"type", +{ +"DuckDB": "0c43dc837a50933667588ef1", +"None": "0c43dc837a50933667588ef1", +"bigquery": "0c43dc837a50933667588ef1", +"databricks": "0c43dc837a50933667588ef1", +"duckdb": "0c43dc837a50933667588ef1", +"generic": "0c43dc837a50933667588ef1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c43dc837a50933667588ef1", +"postgres": "0c43dc837a50933667588ef1", +"postgresql": "0c43dc837a50933667588ef1", +"snowflake": "0c43dc837a50933667588ef1", +"tsql": "0c43dc837a50933667588ef1" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "0c4610f182f18bf41d306e01", +"None": "0c4610f182f18bf41d306e01", +"bigquery": "0c4610f182f18bf41d306e01", +"databricks": "0c4610f182f18bf41d306e01", +"duckdb": "0c4610f182f18bf41d306e01", +"generic": "0c4610f182f18bf41d306e01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c4610f182f18bf41d306e01", +"postgres": "0c4610f182f18bf41d306e01", +"postgresql": "0c4610f182f18bf41d306e01", +"snowflake": "0c4610f182f18bf41d306e01", +"tsql": "0c4610f182f18bf41d306e01" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "1d8b945fd793ecdcacb90c21", +"None": "1d8b945fd793ecdcacb90c21", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "1d8b945fd793ecdcacb90c21", +"generic": "1d8b945fd793ecdcacb90c21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "4c4563a9f306a1bb11e116d9", +"None": "4c4563a9f306a1bb11e116d9", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "4c4563a9f306a1bb11e116d9", +"duckdb": "4c4563a9f306a1bb11e116d9", +"generic": "4c4563a9f306a1bb11e116d9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4c4563a9f306a1bb11e116d9", +"postgres": "4c4563a9f306a1bb11e116d9", +"postgresql": "4c4563a9f306a1bb11e116d9", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "4c4563a9f306a1bb11e116d9" +} +], +[ +"type", +{ +"DuckDB": "29739921db4d47abc1ac18c7", +"None": "29739921db4d47abc1ac18c7", +"bigquery": "01d1b160c1768fa1cd4b2e45", +"databricks": "adbb06204fd66d8071c3dbc6", +"duckdb": "29739921db4d47abc1ac18c7", +"generic": "29739921db4d47abc1ac18c7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "29739921db4d47abc1ac18c7", +"postgres": "29739921db4d47abc1ac18c7", +"postgresql": "29739921db4d47abc1ac18c7", +"snowflake": "216a7f0ead009a087eea4e1c", +"tsql": "561143bc26e834af800f173b" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "ee47e1424cb01deb0cf51760", +"None": "ee47e1424cb01deb0cf51760", +"bigquery": "ee47e1424cb01deb0cf51760", +"databricks": "ee47e1424cb01deb0cf51760", +"duckdb": "ee47e1424cb01deb0cf51760", +"generic": "ee47e1424cb01deb0cf51760", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee47e1424cb01deb0cf51760", +"postgres": "ee47e1424cb01deb0cf51760", +"postgresql": "ee47e1424cb01deb0cf51760", +"snowflake": "ee47e1424cb01deb0cf51760", +"tsql": "ee47e1424cb01deb0cf51760" +} +], +[ +"type", +{ +"DuckDB": "1655128a72b1db9af6a89df3", +"None": "1655128a72b1db9af6a89df3", +"bigquery": "b4c316e81db1cb6a277b5d27", +"databricks": "b4c316e81db1cb6a277b5d27", +"duckdb": "1655128a72b1db9af6a89df3", +"generic": "1655128a72b1db9af6a89df3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b4c316e81db1cb6a277b5d27", +"postgres": "1655128a72b1db9af6a89df3", +"postgresql": "1655128a72b1db9af6a89df3", +"snowflake": "1655128a72b1db9af6a89df3", +"tsql": "1655128a72b1db9af6a89df3" +} +], +[ +"type", +{ +"DuckDB": "768fc4c8dd514e3bc66f6184", +"None": "768fc4c8dd514e3bc66f6184", +"bigquery": "1b5e4ba111b3a98453dd1b5c", +"databricks": "28beac12e88e437078a0c62a", +"duckdb": "768fc4c8dd514e3bc66f6184", +"generic": "768fc4c8dd514e3bc66f6184", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "768fc4c8dd514e3bc66f6184", +"postgres": "768fc4c8dd514e3bc66f6184", +"postgresql": "768fc4c8dd514e3bc66f6184", +"snowflake": "28beac12e88e437078a0c62a", +"tsql": "28beac12e88e437078a0c62a" +} +], +[ +"type", +{ +"DuckDB": "b46dfa976d5ba04f95bea32b", +"None": "b46dfa976d5ba04f95bea32b", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "b46dfa976d5ba04f95bea32b", +"duckdb": "b46dfa976d5ba04f95bea32b", +"generic": "b46dfa976d5ba04f95bea32b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b46dfa976d5ba04f95bea32b", +"postgres": "34dcdfe3e3afe6140e1353e0", +"postgresql": "34dcdfe3e3afe6140e1353e0", +"snowflake": "b46dfa976d5ba04f95bea32b", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "2a0918be53051fe629d9ff8f", +"None": "2a0918be53051fe629d9ff8f", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "2a0918be53051fe629d9ff8f", +"duckdb": "2a0918be53051fe629d9ff8f", +"generic": "2a0918be53051fe629d9ff8f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2a0918be53051fe629d9ff8f", +"postgres": "2a0918be53051fe629d9ff8f", +"postgresql": "2a0918be53051fe629d9ff8f", +"snowflake": "3e611643f0beab4953c4e09b", +"tsql": "2a0918be53051fe629d9ff8f" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "755657fbcced1de4551fb370", +"None": "17a6d1702d6997aa4e447f46", +"bigquery": "17a6d1702d6997aa4e447f46", +"databricks": "17a6d1702d6997aa4e447f46", +"duckdb": "755657fbcced1de4551fb370", +"generic": "17a6d1702d6997aa4e447f46", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "17a6d1702d6997aa4e447f46", +"postgres": "17a6d1702d6997aa4e447f46", +"postgresql": "17a6d1702d6997aa4e447f46", +"snowflake": "17a6d1702d6997aa4e447f46", +"tsql": "17a6d1702d6997aa4e447f46" +} +], +[ +"type", +{ +"DuckDB": "8d66732c326e570c1efcd995", +"None": "8d66732c326e570c1efcd995", +"bigquery": "8d66732c326e570c1efcd995", +"databricks": "8d66732c326e570c1efcd995", +"duckdb": "8d66732c326e570c1efcd995", +"generic": "8d66732c326e570c1efcd995", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8d66732c326e570c1efcd995", +"postgres": "8d66732c326e570c1efcd995", +"postgresql": "8d66732c326e570c1efcd995", +"snowflake": "8d66732c326e570c1efcd995", +"tsql": "8d66732c326e570c1efcd995" +} +], +[ +"type", +{ +"DuckDB": "caf173733defe796376d4efe", +"None": "caf173733defe796376d4efe", +"bigquery": "caf173733defe796376d4efe", +"databricks": "caf173733defe796376d4efe", +"duckdb": "caf173733defe796376d4efe", +"generic": "caf173733defe796376d4efe", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "caf173733defe796376d4efe", +"postgres": "caf173733defe796376d4efe", +"postgresql": "caf173733defe796376d4efe", +"snowflake": "caf173733defe796376d4efe", +"tsql": "caf173733defe796376d4efe" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "14cd6e78d75f61b2bd1bbd71", +"None": "14cd6e78d75f61b2bd1bbd71", +"bigquery": "14cd6e78d75f61b2bd1bbd71", +"databricks": "73504f31156e8e661c1fb26c", +"duckdb": "14cd6e78d75f61b2bd1bbd71", +"generic": "14cd6e78d75f61b2bd1bbd71", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73504f31156e8e661c1fb26c", +"postgres": "14cd6e78d75f61b2bd1bbd71", +"postgresql": "14cd6e78d75f61b2bd1bbd71", +"snowflake": "14cd6e78d75f61b2bd1bbd71", +"tsql": "14cd6e78d75f61b2bd1bbd71" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "cf5e019d9de8b46b75fdfdb6", +"None": "cf5e019d9de8b46b75fdfdb6", +"bigquery": "cf5e019d9de8b46b75fdfdb6", +"databricks": "cf5e019d9de8b46b75fdfdb6", +"duckdb": "cf5e019d9de8b46b75fdfdb6", +"generic": "cf5e019d9de8b46b75fdfdb6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cf5e019d9de8b46b75fdfdb6", +"postgres": "cf5e019d9de8b46b75fdfdb6", +"postgresql": "cf5e019d9de8b46b75fdfdb6", +"snowflake": "cf5e019d9de8b46b75fdfdb6", +"tsql": "cf5e019d9de8b46b75fdfdb6" +} +], +[ +"type", +{ +"DuckDB": "4f8af4650ce9c18ed0cb0bca", +"None": "4f8af4650ce9c18ed0cb0bca", +"bigquery": "4f8af4650ce9c18ed0cb0bca", +"databricks": "4f8af4650ce9c18ed0cb0bca", +"duckdb": "4f8af4650ce9c18ed0cb0bca", +"generic": "4f8af4650ce9c18ed0cb0bca", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4f8af4650ce9c18ed0cb0bca", +"postgres": "4f8af4650ce9c18ed0cb0bca", +"postgresql": "4f8af4650ce9c18ed0cb0bca", +"snowflake": "4f8af4650ce9c18ed0cb0bca", +"tsql": "4f8af4650ce9c18ed0cb0bca" +} +], +[ +"type", +{ +"DuckDB": "cbd04e906c540e91d7c8ea88", +"None": "cbd04e906c540e91d7c8ea88", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "cbd04e906c540e91d7c8ea88", +"duckdb": "cbd04e906c540e91d7c8ea88", +"generic": "cbd04e906c540e91d7c8ea88", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cbd04e906c540e91d7c8ea88", +"postgres": "cbd04e906c540e91d7c8ea88", +"postgresql": "cbd04e906c540e91d7c8ea88", +"snowflake": "cbd04e906c540e91d7c8ea88", +"tsql": "cbd04e906c540e91d7c8ea88" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "8b8d5f75ba6dec68b4cebc6b", +"None": "8b8d5f75ba6dec68b4cebc6b", +"bigquery": "8b8d5f75ba6dec68b4cebc6b", +"databricks": "8b8d5f75ba6dec68b4cebc6b", +"duckdb": "8b8d5f75ba6dec68b4cebc6b", +"generic": "8b8d5f75ba6dec68b4cebc6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8b8d5f75ba6dec68b4cebc6b", +"postgres": "8b8d5f75ba6dec68b4cebc6b", +"postgresql": "8b8d5f75ba6dec68b4cebc6b", +"snowflake": "8b8d5f75ba6dec68b4cebc6b", +"tsql": "8b8d5f75ba6dec68b4cebc6b" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "f30e987a1e4e17305f6863ad", +"None": "036dd6864cf4939023240245", +"bigquery": "036dd6864cf4939023240245", +"databricks": "036dd6864cf4939023240245", +"duckdb": "f30e987a1e4e17305f6863ad", +"generic": "036dd6864cf4939023240245", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "f30e987a1e4e17305f6863ad", +"postgresql": "f30e987a1e4e17305f6863ad", +"snowflake": "ff9e7821f3892ba4ddf70a84", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "bce60580ef3a1dc4ce569fff", +"None": "bce60580ef3a1dc4ce569fff", +"bigquery": "bce60580ef3a1dc4ce569fff", +"databricks": "bce60580ef3a1dc4ce569fff", +"duckdb": "bce60580ef3a1dc4ce569fff", +"generic": "bce60580ef3a1dc4ce569fff", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bce60580ef3a1dc4ce569fff", +"postgres": "bce60580ef3a1dc4ce569fff", +"postgresql": "bce60580ef3a1dc4ce569fff", +"snowflake": "bce60580ef3a1dc4ce569fff", +"tsql": "bce60580ef3a1dc4ce569fff" +} +], +[ +"type", +{ +"DuckDB": "19e127c23b4598727a2d8ceb", +"None": "19e127c23b4598727a2d8ceb", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "19e127c23b4598727a2d8ceb", +"generic": "19e127c23b4598727a2d8ceb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aed0419e7f2ce7c0a8892248", +"postgres": "19e127c23b4598727a2d8ceb", +"postgresql": "19e127c23b4598727a2d8ceb", +"snowflake": "aed0419e7f2ce7c0a8892248", +"tsql": "19e127c23b4598727a2d8ceb" +} +], +[ +"type", +{ +"DuckDB": "558da38ec4d9d5d6c72f60cf", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "558da38ec4d9d5d6c72f60cf", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "c6106f30f23f392e17eadef4", +"None": "c6106f30f23f392e17eadef4", +"bigquery": "c6106f30f23f392e17eadef4", +"databricks": "c6106f30f23f392e17eadef4", +"duckdb": "c6106f30f23f392e17eadef4", +"generic": "c6106f30f23f392e17eadef4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6106f30f23f392e17eadef4", +"postgres": "c6106f30f23f392e17eadef4", +"postgresql": "c6106f30f23f392e17eadef4", +"snowflake": "c6106f30f23f392e17eadef4", +"tsql": "c6106f30f23f392e17eadef4" +} +], +[ +"type", +{ +"DuckDB": "cfb4a49a57197b0df1b2e905", +"None": "cfb4a49a57197b0df1b2e905", +"bigquery": "cfb4a49a57197b0df1b2e905", +"databricks": "cfb4a49a57197b0df1b2e905", +"duckdb": "cfb4a49a57197b0df1b2e905", +"generic": "cfb4a49a57197b0df1b2e905", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cfb4a49a57197b0df1b2e905", +"postgres": "cfb4a49a57197b0df1b2e905", +"postgresql": "cfb4a49a57197b0df1b2e905", +"snowflake": "cfb4a49a57197b0df1b2e905", +"tsql": "cfb4a49a57197b0df1b2e905" +} +], +[ +"type", +{ +"DuckDB": "8790273499dae4e6cbd2d034", +"None": "8790273499dae4e6cbd2d034", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "8790273499dae4e6cbd2d034", +"duckdb": "8790273499dae4e6cbd2d034", +"generic": "8790273499dae4e6cbd2d034", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8790273499dae4e6cbd2d034", +"postgres": "8790273499dae4e6cbd2d034", +"postgresql": "8790273499dae4e6cbd2d034", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "8790273499dae4e6cbd2d034" +} +], +[ +"type", +{ +"DuckDB": "e8605878c6fffc044556a858", +"None": "e8605878c6fffc044556a858", +"bigquery": "e8605878c6fffc044556a858", +"databricks": "e8605878c6fffc044556a858", +"duckdb": "e8605878c6fffc044556a858", +"generic": "e8605878c6fffc044556a858", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8605878c6fffc044556a858", +"postgres": "e8605878c6fffc044556a858", +"postgresql": "e8605878c6fffc044556a858", +"snowflake": "e8605878c6fffc044556a858", +"tsql": "e8605878c6fffc044556a858" +} +], +[ +"type", +{ +"DuckDB": "355fafbc65133795e6c5e43b", +"None": "355fafbc65133795e6c5e43b", +"bigquery": "355fafbc65133795e6c5e43b", +"databricks": "355fafbc65133795e6c5e43b", +"duckdb": "355fafbc65133795e6c5e43b", +"generic": "355fafbc65133795e6c5e43b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "355fafbc65133795e6c5e43b", +"postgres": "355fafbc65133795e6c5e43b", +"postgresql": "355fafbc65133795e6c5e43b", +"snowflake": "355fafbc65133795e6c5e43b", +"tsql": "355fafbc65133795e6c5e43b" +} +], +[ +"type", +{ +"DuckDB": "0dd2a0d5610068e51e271b9f", +"None": "0dd2a0d5610068e51e271b9f", +"bigquery": "0dd2a0d5610068e51e271b9f", +"databricks": "0dd2a0d5610068e51e271b9f", +"duckdb": "0dd2a0d5610068e51e271b9f", +"generic": "0dd2a0d5610068e51e271b9f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dd2a0d5610068e51e271b9f", +"postgres": "0dd2a0d5610068e51e271b9f", +"postgresql": "0dd2a0d5610068e51e271b9f", +"snowflake": "0dd2a0d5610068e51e271b9f", +"tsql": "0dd2a0d5610068e51e271b9f" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "6212562845efbc753cb47471", +"None": "6212562845efbc753cb47471", +"bigquery": "6212562845efbc753cb47471", +"databricks": "6212562845efbc753cb47471", +"duckdb": "6212562845efbc753cb47471", +"generic": "6212562845efbc753cb47471", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6212562845efbc753cb47471", +"postgres": "6212562845efbc753cb47471", +"postgresql": "6212562845efbc753cb47471", +"snowflake": "6212562845efbc753cb47471", +"tsql": "6212562845efbc753cb47471" +} +], +[ +"type", +{ +"DuckDB": "e998cd8f0f74af0526b75ff0", +"None": "e998cd8f0f74af0526b75ff0", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "e998cd8f0f74af0526b75ff0", +"duckdb": "e998cd8f0f74af0526b75ff0", +"generic": "e998cd8f0f74af0526b75ff0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e998cd8f0f74af0526b75ff0", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e998cd8f0f74af0526b75ff0" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "285056becb5d467811be7cd6", +"None": "285056becb5d467811be7cd6", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "285056becb5d467811be7cd6", +"duckdb": "285056becb5d467811be7cd6", +"generic": "285056becb5d467811be7cd6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fc10fc558d906a80a6404b7f", +"postgres": "285056becb5d467811be7cd6", +"postgresql": "285056becb5d467811be7cd6", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "285056becb5d467811be7cd6" +} +], +[ +"type", +{ +"DuckDB": "1655128a72b1db9af6a89df3", +"None": "1655128a72b1db9af6a89df3", +"bigquery": "b4c316e81db1cb6a277b5d27", +"databricks": "b4c316e81db1cb6a277b5d27", +"duckdb": "1655128a72b1db9af6a89df3", +"generic": "1655128a72b1db9af6a89df3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b4c316e81db1cb6a277b5d27", +"postgres": "1655128a72b1db9af6a89df3", +"postgresql": "1655128a72b1db9af6a89df3", +"snowflake": "1655128a72b1db9af6a89df3", +"tsql": "1655128a72b1db9af6a89df3" +} +], +[ +"type", +{ +"DuckDB": "1d8b945fd793ecdcacb90c21", +"None": "1d8b945fd793ecdcacb90c21", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "1d8b945fd793ecdcacb90c21", +"generic": "1d8b945fd793ecdcacb90c21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "3cf4b9eaf303451db16b56f0", +"None": "3cf4b9eaf303451db16b56f0", +"bigquery": "3cf4b9eaf303451db16b56f0", +"databricks": "3cf4b9eaf303451db16b56f0", +"duckdb": "3cf4b9eaf303451db16b56f0", +"generic": "3cf4b9eaf303451db16b56f0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3cf4b9eaf303451db16b56f0", +"postgres": "3cf4b9eaf303451db16b56f0", +"postgresql": "3cf4b9eaf303451db16b56f0", +"snowflake": "3cf4b9eaf303451db16b56f0", +"tsql": "3cf4b9eaf303451db16b56f0" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "1e75e5227bea0ae294ac7af1", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "1e75e5227bea0ae294ac7af1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "19bdd92ea0cc5fb028657510", +"postgresql": "19bdd92ea0cc5fb028657510", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "901e8c830028411846541f65" +} +], +[ +"type", +{ +"DuckDB": "638ca9212b62ada31a448c62", +"None": "1fec266134381c760a254d16", +"bigquery": "1fec266134381c760a254d16", +"databricks": "1fec266134381c760a254d16", +"duckdb": "638ca9212b62ada31a448c62", +"generic": "1fec266134381c760a254d16", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fec266134381c760a254d16", +"postgres": "1fec266134381c760a254d16", +"postgresql": "1fec266134381c760a254d16", +"snowflake": "1fec266134381c760a254d16", +"tsql": "1fec266134381c760a254d16" +} +], +[ +"type", +{ +"DuckDB": "3d23f811a03db519b598c815", +"None": "3d23f811a03db519b598c815", +"bigquery": "3d23f811a03db519b598c815", +"databricks": "3d23f811a03db519b598c815", +"duckdb": "3d23f811a03db519b598c815", +"generic": "3d23f811a03db519b598c815", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d23f811a03db519b598c815", +"postgres": "3d23f811a03db519b598c815", +"postgresql": "3d23f811a03db519b598c815", +"snowflake": "3d23f811a03db519b598c815", +"tsql": "3d23f811a03db519b598c815" +} +], +[ +"type", +{ +"DuckDB": "4928771fd9e503a30457d160", +"None": "4928771fd9e503a30457d160", +"bigquery": "4928771fd9e503a30457d160", +"databricks": "4928771fd9e503a30457d160", +"duckdb": "4928771fd9e503a30457d160", +"generic": "4928771fd9e503a30457d160", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4928771fd9e503a30457d160", +"postgres": "4928771fd9e503a30457d160", +"postgresql": "4928771fd9e503a30457d160", +"snowflake": "4928771fd9e503a30457d160", +"tsql": "4928771fd9e503a30457d160" +} +], +[ +"type", +{ +"DuckDB": "e280c4da8e0aefee0745af65", +"None": "e280c4da8e0aefee0745af65", +"bigquery": "e280c4da8e0aefee0745af65", +"databricks": "e280c4da8e0aefee0745af65", +"duckdb": "e280c4da8e0aefee0745af65", +"generic": "e280c4da8e0aefee0745af65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e280c4da8e0aefee0745af65", +"postgres": "e280c4da8e0aefee0745af65", +"postgresql": "e280c4da8e0aefee0745af65", +"snowflake": "e280c4da8e0aefee0745af65", +"tsql": "e280c4da8e0aefee0745af65" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "5aa51d84ac5e6c7a9306f348", +"bigquery": "5aa51d84ac5e6c7a9306f348", +"databricks": "5aa51d84ac5e6c7a9306f348", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "5aa51d84ac5e6c7a9306f348", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "533aa8bbcadbe01c5e7a5ba7", +"None": "533aa8bbcadbe01c5e7a5ba7", +"bigquery": "533aa8bbcadbe01c5e7a5ba7", +"databricks": "e8a1e0270aa8ec0f1dc6e35e", +"duckdb": "533aa8bbcadbe01c5e7a5ba7", +"generic": "533aa8bbcadbe01c5e7a5ba7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c619dae9f0c9870858ec6530", +"postgres": "533aa8bbcadbe01c5e7a5ba7", +"postgresql": "533aa8bbcadbe01c5e7a5ba7", +"snowflake": "533aa8bbcadbe01c5e7a5ba7", +"tsql": "533aa8bbcadbe01c5e7a5ba7" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "207acc46dcc150502155f4a3", +"None": "d638c2852078bf3facb9dc9e", +"bigquery": "285a15e7c9efb8c0d32d6070", +"databricks": "d638c2852078bf3facb9dc9e", +"duckdb": "207acc46dcc150502155f4a3", +"generic": "d638c2852078bf3facb9dc9e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "6c49aec265f259dd95b5310e", +"postgresql": "6c49aec265f259dd95b5310e", +"snowflake": "ef2c05747b795f92c0b523ee", +"tsql": "2954e90d1d02f8a24fb095ea" +} +], +[ +"type", +{ +"DuckDB": "ad35c4a2523ce8c2f57acf51", +"None": "ad35c4a2523ce8c2f57acf51", +"bigquery": "ad35c4a2523ce8c2f57acf51", +"databricks": "ad35c4a2523ce8c2f57acf51", +"duckdb": "ad35c4a2523ce8c2f57acf51", +"generic": "ad35c4a2523ce8c2f57acf51", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ad35c4a2523ce8c2f57acf51", +"postgres": "ad35c4a2523ce8c2f57acf51", +"postgresql": "ad35c4a2523ce8c2f57acf51", +"snowflake": "ad35c4a2523ce8c2f57acf51", +"tsql": "ad35c4a2523ce8c2f57acf51" +} +], +[ +"type", +{ +"DuckDB": "8203bd5901a8ec71ff7c103e", +"None": "8203bd5901a8ec71ff7c103e", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "8203bd5901a8ec71ff7c103e", +"duckdb": "8203bd5901a8ec71ff7c103e", +"generic": "8203bd5901a8ec71ff7c103e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8203bd5901a8ec71ff7c103e", +"postgres": "8203bd5901a8ec71ff7c103e", +"postgresql": "8203bd5901a8ec71ff7c103e", +"snowflake": "8203bd5901a8ec71ff7c103e", +"tsql": "8203bd5901a8ec71ff7c103e" +} +], +[ +"type", +{ +"DuckDB": "f38ef32a483f1c4f89ae309e", +"None": "f38ef32a483f1c4f89ae309e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f38ef32a483f1c4f89ae309e", +"duckdb": "f38ef32a483f1c4f89ae309e", +"generic": "f38ef32a483f1c4f89ae309e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f38ef32a483f1c4f89ae309e", +"postgres": "f38ef32a483f1c4f89ae309e", +"postgresql": "f38ef32a483f1c4f89ae309e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f38ef32a483f1c4f89ae309e" +} +], +[ +"type", +{ +"DuckDB": "cd932c5de0731b5ff7bddd24", +"None": "cd932c5de0731b5ff7bddd24", +"bigquery": "cd932c5de0731b5ff7bddd24", +"databricks": "cd932c5de0731b5ff7bddd24", +"duckdb": "cd932c5de0731b5ff7bddd24", +"generic": "cd932c5de0731b5ff7bddd24", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cd932c5de0731b5ff7bddd24", +"postgres": "cd932c5de0731b5ff7bddd24", +"postgresql": "cd932c5de0731b5ff7bddd24", +"snowflake": "cd932c5de0731b5ff7bddd24", +"tsql": "cd932c5de0731b5ff7bddd24" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "f38ef32a483f1c4f89ae309e", +"None": "f38ef32a483f1c4f89ae309e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f38ef32a483f1c4f89ae309e", +"duckdb": "f38ef32a483f1c4f89ae309e", +"generic": "f38ef32a483f1c4f89ae309e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f38ef32a483f1c4f89ae309e", +"postgres": "f38ef32a483f1c4f89ae309e", +"postgresql": "f38ef32a483f1c4f89ae309e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f38ef32a483f1c4f89ae309e" +} +], +[ +"type", +{ +"DuckDB": "2e28de65a4b680fb5f4ebda9", +"None": "2e28de65a4b680fb5f4ebda9", +"bigquery": "2e28de65a4b680fb5f4ebda9", +"databricks": "2e28de65a4b680fb5f4ebda9", +"duckdb": "2e28de65a4b680fb5f4ebda9", +"generic": "2e28de65a4b680fb5f4ebda9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2e28de65a4b680fb5f4ebda9", +"postgres": "2e28de65a4b680fb5f4ebda9", +"postgresql": "2e28de65a4b680fb5f4ebda9", +"snowflake": "2e28de65a4b680fb5f4ebda9", +"tsql": "2e28de65a4b680fb5f4ebda9" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "ace891b6844cb59efad42031", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "ace891b6844cb59efad42031", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "57922064a7dc546cbb50e42b", +"None": "57922064a7dc546cbb50e42b", +"bigquery": "57922064a7dc546cbb50e42b", +"databricks": "57922064a7dc546cbb50e42b", +"duckdb": "57922064a7dc546cbb50e42b", +"generic": "57922064a7dc546cbb50e42b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "57922064a7dc546cbb50e42b", +"postgres": "57922064a7dc546cbb50e42b", +"postgresql": "57922064a7dc546cbb50e42b", +"snowflake": "57922064a7dc546cbb50e42b", +"tsql": "57922064a7dc546cbb50e42b" +} +], +[ +"type", +{ +"DuckDB": "e08a957777bd278f921ca0ee", +"None": "e08a957777bd278f921ca0ee", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "e08a957777bd278f921ca0ee", +"duckdb": "e08a957777bd278f921ca0ee", +"generic": "e08a957777bd278f921ca0ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e08a957777bd278f921ca0ee", +"postgres": "e08a957777bd278f921ca0ee", +"postgresql": "e08a957777bd278f921ca0ee", +"snowflake": "e08a957777bd278f921ca0ee", +"tsql": "e08a957777bd278f921ca0ee" +} +], +[ +"type", +{ +"DuckDB": "5980124ff4dad5573f810a21", +"None": "2785bf366213936ce93d91f2", +"bigquery": "2785bf366213936ce93d91f2", +"databricks": "2785bf366213936ce93d91f2", +"duckdb": "5980124ff4dad5573f810a21", +"generic": "2785bf366213936ce93d91f2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "5980124ff4dad5573f810a21", +"postgresql": "5980124ff4dad5573f810a21", +"snowflake": "b53e32783b8b9274e350988b", +"tsql": "6c658d2325cc2559c4253e41" +} +], +[ +"type", +{ +"DuckDB": "055c9340448c8378561480dd", +"None": "055c9340448c8378561480dd", +"bigquery": "055c9340448c8378561480dd", +"databricks": "055c9340448c8378561480dd", +"duckdb": "055c9340448c8378561480dd", +"generic": "055c9340448c8378561480dd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "055c9340448c8378561480dd", +"postgres": "055c9340448c8378561480dd", +"postgresql": "055c9340448c8378561480dd", +"snowflake": "055c9340448c8378561480dd", +"tsql": "055c9340448c8378561480dd" +} +], +[ +"type", +{ +"DuckDB": "ab8c248625776c7ac340119c", +"None": "ab8c248625776c7ac340119c", +"bigquery": "ab8c248625776c7ac340119c", +"databricks": "ab8c248625776c7ac340119c", +"duckdb": "ab8c248625776c7ac340119c", +"generic": "ab8c248625776c7ac340119c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab8c248625776c7ac340119c", +"postgres": "ab8c248625776c7ac340119c", +"postgresql": "ab8c248625776c7ac340119c", +"snowflake": "ab8c248625776c7ac340119c", +"tsql": "ab8c248625776c7ac340119c" +} +], +[ +"type", +{ +"DuckDB": "d23bf939a7eb8e49c7e6867a", +"None": "d23bf939a7eb8e49c7e6867a", +"bigquery": "d23bf939a7eb8e49c7e6867a", +"databricks": "d23bf939a7eb8e49c7e6867a", +"duckdb": "d23bf939a7eb8e49c7e6867a", +"generic": "d23bf939a7eb8e49c7e6867a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d23bf939a7eb8e49c7e6867a", +"postgres": "d23bf939a7eb8e49c7e6867a", +"postgresql": "d23bf939a7eb8e49c7e6867a", +"snowflake": "d23bf939a7eb8e49c7e6867a", +"tsql": "d23bf939a7eb8e49c7e6867a" +} +], +[ +"type", +{ +"DuckDB": "d79c3ac70093cb4cb531e71c", +"None": "d79c3ac70093cb4cb531e71c", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "d79c3ac70093cb4cb531e71c", +"duckdb": "d79c3ac70093cb4cb531e71c", +"generic": "d79c3ac70093cb4cb531e71c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d79c3ac70093cb4cb531e71c", +"postgres": "d79c3ac70093cb4cb531e71c", +"postgresql": "d79c3ac70093cb4cb531e71c", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d79c3ac70093cb4cb531e71c" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "782ae75a1700e1a48355b077", +"None": "782ae75a1700e1a48355b077", +"bigquery": "782ae75a1700e1a48355b077", +"databricks": "782ae75a1700e1a48355b077", +"duckdb": "782ae75a1700e1a48355b077", +"generic": "782ae75a1700e1a48355b077", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "782ae75a1700e1a48355b077", +"postgres": "782ae75a1700e1a48355b077", +"postgresql": "782ae75a1700e1a48355b077", +"snowflake": "782ae75a1700e1a48355b077", +"tsql": "782ae75a1700e1a48355b077" +} +], +[ +"type", +{ +"DuckDB": "28eb0d6de9724959ce5ba43e", +"None": "28eb0d6de9724959ce5ba43e", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "28eb0d6de9724959ce5ba43e", +"duckdb": "28eb0d6de9724959ce5ba43e", +"generic": "28eb0d6de9724959ce5ba43e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "28eb0d6de9724959ce5ba43e", +"postgresql": "28eb0d6de9724959ce5ba43e", +"snowflake": "28eb0d6de9724959ce5ba43e", +"tsql": "49cbf3d201d5d03a3e0154a0" +} +], +[ +"type", +{ +"DuckDB": "dcd1c844f2d13f3b6788d1a6", +"None": "dcd1c844f2d13f3b6788d1a6", +"bigquery": "dcd1c844f2d13f3b6788d1a6", +"databricks": "dcd1c844f2d13f3b6788d1a6", +"duckdb": "dcd1c844f2d13f3b6788d1a6", +"generic": "dcd1c844f2d13f3b6788d1a6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dcd1c844f2d13f3b6788d1a6", +"postgres": "dcd1c844f2d13f3b6788d1a6", +"postgresql": "dcd1c844f2d13f3b6788d1a6", +"snowflake": "dcd1c844f2d13f3b6788d1a6", +"tsql": "dcd1c844f2d13f3b6788d1a6" +} +], +[ +"type", +{ +"DuckDB": "304be1d51ad99bfab3750f57", +"None": "304be1d51ad99bfab3750f57", +"bigquery": "304be1d51ad99bfab3750f57", +"databricks": "304be1d51ad99bfab3750f57", +"duckdb": "304be1d51ad99bfab3750f57", +"generic": "304be1d51ad99bfab3750f57", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "304be1d51ad99bfab3750f57", +"postgres": "304be1d51ad99bfab3750f57", +"postgresql": "304be1d51ad99bfab3750f57", +"snowflake": "304be1d51ad99bfab3750f57", +"tsql": "304be1d51ad99bfab3750f57" +} +], +[ +"type", +{ +"DuckDB": "7466f0581624f38e4d1e972d", +"None": "f25507ff493aa410f2d2a6d6", +"bigquery": "7aa62aca0969d2a178982985", +"databricks": "3e42d732ee33bbd1424911a7", +"duckdb": "7466f0581624f38e4d1e972d", +"generic": "f25507ff493aa410f2d2a6d6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f25507ff493aa410f2d2a6d6", +"postgres": "f25507ff493aa410f2d2a6d6", +"postgresql": "f25507ff493aa410f2d2a6d6", +"snowflake": "60ad8b0a5cb3592bd3b0fc8f", +"tsql": "f25507ff493aa410f2d2a6d6" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "2427f5522575cdc3d6a74e4c", +"None": "2427f5522575cdc3d6a74e4c", +"bigquery": "2427f5522575cdc3d6a74e4c", +"databricks": "2427f5522575cdc3d6a74e4c", +"duckdb": "2427f5522575cdc3d6a74e4c", +"generic": "2427f5522575cdc3d6a74e4c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2427f5522575cdc3d6a74e4c", +"postgres": "2427f5522575cdc3d6a74e4c", +"postgresql": "2427f5522575cdc3d6a74e4c", +"snowflake": "2427f5522575cdc3d6a74e4c", +"tsql": "2427f5522575cdc3d6a74e4c" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "3af3f9124a8649e4f72030c1", +"None": "3af3f9124a8649e4f72030c1", +"bigquery": "8191fff105ab0743bf9ffd39", +"databricks": "8191fff105ab0743bf9ffd39", +"duckdb": "3af3f9124a8649e4f72030c1", +"generic": "3af3f9124a8649e4f72030c1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8191fff105ab0743bf9ffd39", +"postgres": "3af3f9124a8649e4f72030c1", +"postgresql": "3af3f9124a8649e4f72030c1", +"snowflake": "3af3f9124a8649e4f72030c1", +"tsql": "3af3f9124a8649e4f72030c1" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "5c28019dce48a74708c90a76", +"None": "5c28019dce48a74708c90a76", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "5c28019dce48a74708c90a76", +"duckdb": "5c28019dce48a74708c90a76", +"generic": "5c28019dce48a74708c90a76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "5c28019dce48a74708c90a76", +"postgresql": "5c28019dce48a74708c90a76", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "2a7a7132adc795ca2bc39c37" +} +], +[ +"type", +{ +"DuckDB": "0dcd6a19b7dc6f6b6d4a5006", +"None": "0dcd6a19b7dc6f6b6d4a5006", +"bigquery": "0dcd6a19b7dc6f6b6d4a5006", +"databricks": "0dcd6a19b7dc6f6b6d4a5006", +"duckdb": "0dcd6a19b7dc6f6b6d4a5006", +"generic": "0dcd6a19b7dc6f6b6d4a5006", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dcd6a19b7dc6f6b6d4a5006", +"postgres": "0dcd6a19b7dc6f6b6d4a5006", +"postgresql": "0dcd6a19b7dc6f6b6d4a5006", +"snowflake": "0dcd6a19b7dc6f6b6d4a5006", +"tsql": "0dcd6a19b7dc6f6b6d4a5006" +} +], +[ +"type", +{ +"DuckDB": "c2700ca3573352821e09af03", +"None": "c2700ca3573352821e09af03", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c2700ca3573352821e09af03", +"duckdb": "c2700ca3573352821e09af03", +"generic": "c2700ca3573352821e09af03", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c2700ca3573352821e09af03", +"postgres": "c2700ca3573352821e09af03", +"postgresql": "c2700ca3573352821e09af03", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e80839f7b11a9775b7596b50" +} +], +[ +"type", +{ +"DuckDB": "787213ce6b6e210ded1d752c", +"None": "787213ce6b6e210ded1d752c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "787213ce6b6e210ded1d752c", +"duckdb": "787213ce6b6e210ded1d752c", +"generic": "787213ce6b6e210ded1d752c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "787213ce6b6e210ded1d752c", +"postgres": "787213ce6b6e210ded1d752c", +"postgresql": "787213ce6b6e210ded1d752c", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "787213ce6b6e210ded1d752c" +} +], +[ +"type", +{ +"DuckDB": "d7b74701deb8b87c32deb7a6", +"None": "d7b74701deb8b87c32deb7a6", +"bigquery": "d7b74701deb8b87c32deb7a6", +"databricks": "d7b74701deb8b87c32deb7a6", +"duckdb": "d7b74701deb8b87c32deb7a6", +"generic": "d7b74701deb8b87c32deb7a6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d7b74701deb8b87c32deb7a6", +"postgres": "d7b74701deb8b87c32deb7a6", +"postgresql": "d7b74701deb8b87c32deb7a6", +"snowflake": "d7b74701deb8b87c32deb7a6", +"tsql": "d7b74701deb8b87c32deb7a6" +} +], +[ +"type", +{ +"DuckDB": "a6b64a929834c52f65384447", +"None": "a6b64a929834c52f65384447", +"bigquery": "a6b64a929834c52f65384447", +"databricks": "a6b64a929834c52f65384447", +"duckdb": "a6b64a929834c52f65384447", +"generic": "a6b64a929834c52f65384447", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a6b64a929834c52f65384447", +"postgres": "a6b64a929834c52f65384447", +"postgresql": "a6b64a929834c52f65384447", +"snowflake": "a6b64a929834c52f65384447", +"tsql": "a6b64a929834c52f65384447" +} +], +[ +"type", +{ +"DuckDB": "638ca9212b62ada31a448c62", +"None": "1fec266134381c760a254d16", +"bigquery": "1fec266134381c760a254d16", +"databricks": "1fec266134381c760a254d16", +"duckdb": "638ca9212b62ada31a448c62", +"generic": "1fec266134381c760a254d16", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fec266134381c760a254d16", +"postgres": "1fec266134381c760a254d16", +"postgresql": "1fec266134381c760a254d16", +"snowflake": "1fec266134381c760a254d16", +"tsql": "1fec266134381c760a254d16" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "fbbf6dbffd81c1b899b2f41b", +"None": "fbbf6dbffd81c1b899b2f41b", +"bigquery": "fbbf6dbffd81c1b899b2f41b", +"databricks": "fbbf6dbffd81c1b899b2f41b", +"duckdb": "fbbf6dbffd81c1b899b2f41b", +"generic": "fbbf6dbffd81c1b899b2f41b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fbbf6dbffd81c1b899b2f41b", +"postgres": "fbbf6dbffd81c1b899b2f41b", +"postgresql": "fbbf6dbffd81c1b899b2f41b", +"snowflake": "fbbf6dbffd81c1b899b2f41b", +"tsql": "fbbf6dbffd81c1b899b2f41b" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "a2c63b7e92b578944818d750", +"None": "a2c63b7e92b578944818d750", +"bigquery": "a2c63b7e92b578944818d750", +"databricks": "a2c63b7e92b578944818d750", +"duckdb": "a2c63b7e92b578944818d750", +"generic": "a2c63b7e92b578944818d750", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a2c63b7e92b578944818d750", +"postgres": "a2c63b7e92b578944818d750", +"postgresql": "a2c63b7e92b578944818d750", +"snowflake": "a2c63b7e92b578944818d750", +"tsql": "a2c63b7e92b578944818d750" +} +], +[ +"type", +{ +"DuckDB": "a3d6f52ec64a728636793faa", +"None": "a3d6f52ec64a728636793faa", +"bigquery": "a3d6f52ec64a728636793faa", +"databricks": "a3d6f52ec64a728636793faa", +"duckdb": "a3d6f52ec64a728636793faa", +"generic": "a3d6f52ec64a728636793faa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a3d6f52ec64a728636793faa", +"postgres": "a3d6f52ec64a728636793faa", +"postgresql": "a3d6f52ec64a728636793faa", +"snowflake": "a3d6f52ec64a728636793faa", +"tsql": "a3d6f52ec64a728636793faa" +} +], +[ +"type", +{ +"DuckDB": "c7b697bbfa82dd2821ad64e9", +"None": "c7b697bbfa82dd2821ad64e9", +"bigquery": "dcb00b7053ffe69fe3ce99cd", +"databricks": "dcb00b7053ffe69fe3ce99cd", +"duckdb": "c7b697bbfa82dd2821ad64e9", +"generic": "c7b697bbfa82dd2821ad64e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dcb00b7053ffe69fe3ce99cd", +"postgres": "c7b697bbfa82dd2821ad64e9", +"postgresql": "c7b697bbfa82dd2821ad64e9", +"snowflake": "c7b697bbfa82dd2821ad64e9", +"tsql": "c7b697bbfa82dd2821ad64e9" +} +], +[ +"type", +{ +"DuckDB": "717cf79ef777823eb3576deb", +"None": "717cf79ef777823eb3576deb", +"bigquery": "717cf79ef777823eb3576deb", +"databricks": "717cf79ef777823eb3576deb", +"duckdb": "717cf79ef777823eb3576deb", +"generic": "717cf79ef777823eb3576deb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "717cf79ef777823eb3576deb", +"postgres": "717cf79ef777823eb3576deb", +"postgresql": "717cf79ef777823eb3576deb", +"snowflake": "717cf79ef777823eb3576deb", +"tsql": "717cf79ef777823eb3576deb" +} +], +[ +"type", +{ +"DuckDB": "c1dad3a1ff61e56cb10b9cdf", +"None": "c1dad3a1ff61e56cb10b9cdf", +"bigquery": "c1dad3a1ff61e56cb10b9cdf", +"databricks": "c1dad3a1ff61e56cb10b9cdf", +"duckdb": "c1dad3a1ff61e56cb10b9cdf", +"generic": "c1dad3a1ff61e56cb10b9cdf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c1dad3a1ff61e56cb10b9cdf", +"postgres": "c1dad3a1ff61e56cb10b9cdf", +"postgresql": "c1dad3a1ff61e56cb10b9cdf", +"snowflake": "c1dad3a1ff61e56cb10b9cdf", +"tsql": "c1dad3a1ff61e56cb10b9cdf" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "755657fbcced1de4551fb370", +"None": "17a6d1702d6997aa4e447f46", +"bigquery": "17a6d1702d6997aa4e447f46", +"databricks": "17a6d1702d6997aa4e447f46", +"duckdb": "755657fbcced1de4551fb370", +"generic": "17a6d1702d6997aa4e447f46", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "17a6d1702d6997aa4e447f46", +"postgres": "17a6d1702d6997aa4e447f46", +"postgresql": "17a6d1702d6997aa4e447f46", +"snowflake": "17a6d1702d6997aa4e447f46", +"tsql": "17a6d1702d6997aa4e447f46" +} +], +[ +"type", +{ +"DuckDB": "c05190b1f2b5d7003e7076da", +"None": "c05190b1f2b5d7003e7076da", +"bigquery": "c05190b1f2b5d7003e7076da", +"databricks": "c05190b1f2b5d7003e7076da", +"duckdb": "c05190b1f2b5d7003e7076da", +"generic": "c05190b1f2b5d7003e7076da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c05190b1f2b5d7003e7076da", +"postgres": "c05190b1f2b5d7003e7076da", +"postgresql": "c05190b1f2b5d7003e7076da", +"snowflake": "c05190b1f2b5d7003e7076da", +"tsql": "c05190b1f2b5d7003e7076da" +} +], +[ +"type", +{ +"DuckDB": "fa45b7e4a9ef3b5a9c5781af", +"None": "fa45b7e4a9ef3b5a9c5781af", +"bigquery": "fa45b7e4a9ef3b5a9c5781af", +"databricks": "fa45b7e4a9ef3b5a9c5781af", +"duckdb": "fa45b7e4a9ef3b5a9c5781af", +"generic": "fa45b7e4a9ef3b5a9c5781af", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fa45b7e4a9ef3b5a9c5781af", +"postgres": "fa45b7e4a9ef3b5a9c5781af", +"postgresql": "fa45b7e4a9ef3b5a9c5781af", +"snowflake": "fa45b7e4a9ef3b5a9c5781af", +"tsql": "fa45b7e4a9ef3b5a9c5781af" +} +], +[ +"type", +{ +"DuckDB": "72bd5e38b32893397eb51579", +"None": "1bdf5908ca249b2331a0427c", +"bigquery": "1bdf5908ca249b2331a0427c", +"databricks": "1bdf5908ca249b2331a0427c", +"duckdb": "72bd5e38b32893397eb51579", +"generic": "1bdf5908ca249b2331a0427c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1bdf5908ca249b2331a0427c", +"postgres": "1bdf5908ca249b2331a0427c", +"postgresql": "1bdf5908ca249b2331a0427c", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "1bdf5908ca249b2331a0427c" +} +], +[ +"type", +{ +"DuckDB": "bb7975dcb1f2ac7cdd149b0b", +"None": "bb7975dcb1f2ac7cdd149b0b", +"bigquery": "bb7975dcb1f2ac7cdd149b0b", +"databricks": "bb7975dcb1f2ac7cdd149b0b", +"duckdb": "bb7975dcb1f2ac7cdd149b0b", +"generic": "bb7975dcb1f2ac7cdd149b0b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bb7975dcb1f2ac7cdd149b0b", +"postgres": "bb7975dcb1f2ac7cdd149b0b", +"postgresql": "bb7975dcb1f2ac7cdd149b0b", +"snowflake": "bb7975dcb1f2ac7cdd149b0b", +"tsql": "bb7975dcb1f2ac7cdd149b0b" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "74ed18299daa6faf5c9533e2", +"None": "74ed18299daa6faf5c9533e2", +"bigquery": "74ed18299daa6faf5c9533e2", +"databricks": "74ed18299daa6faf5c9533e2", +"duckdb": "74ed18299daa6faf5c9533e2", +"generic": "74ed18299daa6faf5c9533e2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "74ed18299daa6faf5c9533e2", +"postgres": "74ed18299daa6faf5c9533e2", +"postgresql": "74ed18299daa6faf5c9533e2", +"snowflake": "74ed18299daa6faf5c9533e2", +"tsql": "74ed18299daa6faf5c9533e2" +} +], +[ +"type", +{ +"DuckDB": "60a23b7dcae6c79f4de83068", +"None": "60a23b7dcae6c79f4de83068", +"bigquery": "60a23b7dcae6c79f4de83068", +"databricks": "60a23b7dcae6c79f4de83068", +"duckdb": "60a23b7dcae6c79f4de83068", +"generic": "60a23b7dcae6c79f4de83068", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60a23b7dcae6c79f4de83068", +"postgres": "60a23b7dcae6c79f4de83068", +"postgresql": "60a23b7dcae6c79f4de83068", +"snowflake": "60a23b7dcae6c79f4de83068", +"tsql": "60a23b7dcae6c79f4de83068" +} +], +[ +"type", +{ +"DuckDB": "0f799a95a4f5ef502897f02c", +"None": "0f799a95a4f5ef502897f02c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "0f799a95a4f5ef502897f02c", +"duckdb": "0f799a95a4f5ef502897f02c", +"generic": "0f799a95a4f5ef502897f02c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0f799a95a4f5ef502897f02c", +"postgres": "0f799a95a4f5ef502897f02c", +"postgresql": "0f799a95a4f5ef502897f02c", +"snowflake": "0f799a95a4f5ef502897f02c", +"tsql": "0f799a95a4f5ef502897f02c" +} +], +[ +"type", +{ +"DuckDB": "5d0a19a893dbf0c1e6e9f15c", +"None": "5d0a19a893dbf0c1e6e9f15c", +"bigquery": "5d0a19a893dbf0c1e6e9f15c", +"databricks": "5d0a19a893dbf0c1e6e9f15c", +"duckdb": "5d0a19a893dbf0c1e6e9f15c", +"generic": "5d0a19a893dbf0c1e6e9f15c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5d0a19a893dbf0c1e6e9f15c", +"postgres": "5d0a19a893dbf0c1e6e9f15c", +"postgresql": "5d0a19a893dbf0c1e6e9f15c", +"snowflake": "5d0a19a893dbf0c1e6e9f15c", +"tsql": "5d0a19a893dbf0c1e6e9f15c" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "855e2727b4a8cb5d2711f102", +"None": "855e2727b4a8cb5d2711f102", +"bigquery": "855e2727b4a8cb5d2711f102", +"databricks": "855e2727b4a8cb5d2711f102", +"duckdb": "855e2727b4a8cb5d2711f102", +"generic": "855e2727b4a8cb5d2711f102", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "855e2727b4a8cb5d2711f102", +"postgres": "855e2727b4a8cb5d2711f102", +"postgresql": "855e2727b4a8cb5d2711f102", +"snowflake": "855e2727b4a8cb5d2711f102", +"tsql": "855e2727b4a8cb5d2711f102" +} +], +[ +"type", +{ +"DuckDB": "1ebac487198b6c90b027faab", +"None": "1ebac487198b6c90b027faab", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "1ebac487198b6c90b027faab", +"duckdb": "1ebac487198b6c90b027faab", +"generic": "1ebac487198b6c90b027faab", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1ebac487198b6c90b027faab", +"postgres": "1ebac487198b6c90b027faab", +"postgresql": "1ebac487198b6c90b027faab", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c692d974ee3f362ecb38f27c" +} +], +[ +"type", +{ +"DuckDB": "9c2cff8be7225cf79883ca65", +"None": "9c2cff8be7225cf79883ca65", +"bigquery": "9c2cff8be7225cf79883ca65", +"databricks": "901ac89f8d9ec18bd3a61e4b", +"duckdb": "9c2cff8be7225cf79883ca65", +"generic": "9c2cff8be7225cf79883ca65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "901ac89f8d9ec18bd3a61e4b", +"postgres": "9c2cff8be7225cf79883ca65", +"postgresql": "9c2cff8be7225cf79883ca65", +"snowflake": "9c2cff8be7225cf79883ca65", +"tsql": "9c2cff8be7225cf79883ca65" +} +], +[ +"type", +{ +"DuckDB": "74f50f8ec759a3cc65ac027d", +"None": "74f50f8ec759a3cc65ac027d", +"bigquery": "74f50f8ec759a3cc65ac027d", +"databricks": "74f50f8ec759a3cc65ac027d", +"duckdb": "74f50f8ec759a3cc65ac027d", +"generic": "74f50f8ec759a3cc65ac027d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "74f50f8ec759a3cc65ac027d", +"postgres": "74f50f8ec759a3cc65ac027d", +"postgresql": "74f50f8ec759a3cc65ac027d", +"snowflake": "74f50f8ec759a3cc65ac027d", +"tsql": "74f50f8ec759a3cc65ac027d" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "0dcd6a19b7dc6f6b6d4a5006", +"None": "0dcd6a19b7dc6f6b6d4a5006", +"bigquery": "0dcd6a19b7dc6f6b6d4a5006", +"databricks": "0dcd6a19b7dc6f6b6d4a5006", +"duckdb": "0dcd6a19b7dc6f6b6d4a5006", +"generic": "0dcd6a19b7dc6f6b6d4a5006", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dcd6a19b7dc6f6b6d4a5006", +"postgres": "0dcd6a19b7dc6f6b6d4a5006", +"postgresql": "0dcd6a19b7dc6f6b6d4a5006", +"snowflake": "0dcd6a19b7dc6f6b6d4a5006", +"tsql": "0dcd6a19b7dc6f6b6d4a5006" +} +], +[ +"type", +{ +"DuckDB": "7ffb4db35f5f9871e8c9a084", +"None": "7ffb4db35f5f9871e8c9a084", +"bigquery": "7ffb4db35f5f9871e8c9a084", +"databricks": "7ffb4db35f5f9871e8c9a084", +"duckdb": "7ffb4db35f5f9871e8c9a084", +"generic": "7ffb4db35f5f9871e8c9a084", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7ffb4db35f5f9871e8c9a084", +"postgres": "7ffb4db35f5f9871e8c9a084", +"postgresql": "7ffb4db35f5f9871e8c9a084", +"snowflake": "7ffb4db35f5f9871e8c9a084", +"tsql": "7ffb4db35f5f9871e8c9a084" +} +], +[ +"type", +{ +"DuckDB": "d79c3ac70093cb4cb531e71c", +"None": "d79c3ac70093cb4cb531e71c", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "d79c3ac70093cb4cb531e71c", +"duckdb": "d79c3ac70093cb4cb531e71c", +"generic": "d79c3ac70093cb4cb531e71c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d79c3ac70093cb4cb531e71c", +"postgres": "d79c3ac70093cb4cb531e71c", +"postgresql": "d79c3ac70093cb4cb531e71c", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d79c3ac70093cb4cb531e71c" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "7afe04283391fb5bd9bd1f6e", +"None": "6fad076e9d13c0d09c490402", +"bigquery": "6fad076e9d13c0d09c490402", +"databricks": "02e0b2ed25f32a8d09350a23", +"duckdb": "7afe04283391fb5bd9bd1f6e", +"generic": "6fad076e9d13c0d09c490402", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "7afe04283391fb5bd9bd1f6e", +"postgresql": "7afe04283391fb5bd9bd1f6e", +"snowflake": "dddc37362726cca4f2353a8d", +"tsql": "25469e79d5519fb84076e220" +} +], +[ +"type", +{ +"DuckDB": "802f1c1fcb97ddd5679172d2", +"None": "802f1c1fcb97ddd5679172d2", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "802f1c1fcb97ddd5679172d2", +"duckdb": "802f1c1fcb97ddd5679172d2", +"generic": "802f1c1fcb97ddd5679172d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "802f1c1fcb97ddd5679172d2", +"postgres": "802f1c1fcb97ddd5679172d2", +"postgresql": "802f1c1fcb97ddd5679172d2", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "08784560ad78e14a02cc066f" +} +], +[ +"type", +{ +"DuckDB": "71cce31a35d7039dcd2aa86d", +"None": "71cce31a35d7039dcd2aa86d", +"bigquery": "04f1dc31c6fcdb2b73b4e4f3", +"databricks": "7552f1f36764fa5333dc3b01", +"duckdb": "71cce31a35d7039dcd2aa86d", +"generic": "71cce31a35d7039dcd2aa86d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "71cce31a35d7039dcd2aa86d", +"postgres": "71cce31a35d7039dcd2aa86d", +"postgresql": "71cce31a35d7039dcd2aa86d", +"snowflake": "aa1ac5b2c999b497d54fa520", +"tsql": "71cce31a35d7039dcd2aa86d" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "4169d9651c2a3667fc561f32", +"None": "4169d9651c2a3667fc561f32", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "4169d9651c2a3667fc561f32", +"duckdb": "4169d9651c2a3667fc561f32", +"generic": "4169d9651c2a3667fc561f32", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4169d9651c2a3667fc561f32", +"postgres": "4169d9651c2a3667fc561f32", +"postgresql": "4169d9651c2a3667fc561f32", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "4169d9651c2a3667fc561f32" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "6533f23a0660791970dbbbc8", +"None": "6533f23a0660791970dbbbc8", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "6533f23a0660791970dbbbc8", +"duckdb": "6533f23a0660791970dbbbc8", +"generic": "6533f23a0660791970dbbbc8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6533f23a0660791970dbbbc8", +"postgres": "6533f23a0660791970dbbbc8", +"postgresql": "6533f23a0660791970dbbbc8", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "6533f23a0660791970dbbbc8" +} +], +[ +"type", +{ +"DuckDB": "9d0ae138704cb97e3fe4d105", +"None": "9d0ae138704cb97e3fe4d105", +"bigquery": "6d07fc2f489d414e7eb90262", +"databricks": "6d07fc2f489d414e7eb90262", +"duckdb": "9d0ae138704cb97e3fe4d105", +"generic": "9d0ae138704cb97e3fe4d105", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6d07fc2f489d414e7eb90262", +"postgres": "9d0ae138704cb97e3fe4d105", +"postgresql": "9d0ae138704cb97e3fe4d105", +"snowflake": "9d0ae138704cb97e3fe4d105", +"tsql": "9d0ae138704cb97e3fe4d105" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "2c44dc35fd1ef8701101802e", +"None": "2c44dc35fd1ef8701101802e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "2c44dc35fd1ef8701101802e", +"duckdb": "2c44dc35fd1ef8701101802e", +"generic": "2c44dc35fd1ef8701101802e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c44dc35fd1ef8701101802e", +"postgres": "2c44dc35fd1ef8701101802e", +"postgresql": "2c44dc35fd1ef8701101802e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "2c44dc35fd1ef8701101802e" +} +], +[ +"type", +{ +"DuckDB": "9917d6c8c04307304904313b", +"None": "9917d6c8c04307304904313b", +"bigquery": "9917d6c8c04307304904313b", +"databricks": "9917d6c8c04307304904313b", +"duckdb": "9917d6c8c04307304904313b", +"generic": "9917d6c8c04307304904313b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9917d6c8c04307304904313b", +"postgres": "9917d6c8c04307304904313b", +"postgresql": "9917d6c8c04307304904313b", +"snowflake": "9917d6c8c04307304904313b", +"tsql": "9917d6c8c04307304904313b" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "e51e1eecca3e36c76e34498f", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "e51e1eecca3e36c76e34498f", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "e51e1eecca3e36c76e34498f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "84fca9ac893777959cfd715c", +"postgres": "e51e1eecca3e36c76e34498f", +"postgresql": "e51e1eecca3e36c76e34498f", +"snowflake": "e51e1eecca3e36c76e34498f", +"tsql": "8adf69b4c11191257f2953cc" +} +], +[ +"type", +{ +"DuckDB": "4b356542044f1267fcfc487a", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "4b356542044f1267fcfc487a", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "afc451102e0ebe7f507119b9", +"None": "afc451102e0ebe7f507119b9", +"bigquery": "afc451102e0ebe7f507119b9", +"databricks": "afc451102e0ebe7f507119b9", +"duckdb": "afc451102e0ebe7f507119b9", +"generic": "afc451102e0ebe7f507119b9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "afc451102e0ebe7f507119b9", +"postgres": "afc451102e0ebe7f507119b9", +"postgresql": "afc451102e0ebe7f507119b9", +"snowflake": "afc451102e0ebe7f507119b9", +"tsql": "afc451102e0ebe7f507119b9" +} +], +[ +"type", +{ +"DuckDB": "74ed18299daa6faf5c9533e2", +"None": "74ed18299daa6faf5c9533e2", +"bigquery": "74ed18299daa6faf5c9533e2", +"databricks": "74ed18299daa6faf5c9533e2", +"duckdb": "74ed18299daa6faf5c9533e2", +"generic": "74ed18299daa6faf5c9533e2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "74ed18299daa6faf5c9533e2", +"postgres": "74ed18299daa6faf5c9533e2", +"postgresql": "74ed18299daa6faf5c9533e2", +"snowflake": "74ed18299daa6faf5c9533e2", +"tsql": "74ed18299daa6faf5c9533e2" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "60b39a68a18438603dd5496d", +"None": "60b39a68a18438603dd5496d", +"bigquery": "60b39a68a18438603dd5496d", +"databricks": "60b39a68a18438603dd5496d", +"duckdb": "60b39a68a18438603dd5496d", +"generic": "60b39a68a18438603dd5496d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60b39a68a18438603dd5496d", +"postgres": "60b39a68a18438603dd5496d", +"postgresql": "60b39a68a18438603dd5496d", +"snowflake": "60b39a68a18438603dd5496d", +"tsql": "60b39a68a18438603dd5496d" +} +], +[ +"type", +{ +"DuckDB": "b1b23029bedaa16e2a6f8516", +"None": "b1b23029bedaa16e2a6f8516", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "b1b23029bedaa16e2a6f8516", +"duckdb": "b1b23029bedaa16e2a6f8516", +"generic": "b1b23029bedaa16e2a6f8516", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b1b23029bedaa16e2a6f8516", +"postgres": "b1b23029bedaa16e2a6f8516", +"postgresql": "b1b23029bedaa16e2a6f8516", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "b1b23029bedaa16e2a6f8516" +} +], +[ +"type", +{ +"DuckDB": "0b9d9879e289ea9a6a3e3e70", +"None": "0b9d9879e289ea9a6a3e3e70", +"bigquery": "0b9d9879e289ea9a6a3e3e70", +"databricks": "0b9d9879e289ea9a6a3e3e70", +"duckdb": "0b9d9879e289ea9a6a3e3e70", +"generic": "0b9d9879e289ea9a6a3e3e70", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b9d9879e289ea9a6a3e3e70", +"postgres": "0b9d9879e289ea9a6a3e3e70", +"postgresql": "0b9d9879e289ea9a6a3e3e70", +"snowflake": "0b9d9879e289ea9a6a3e3e70", +"tsql": "0b9d9879e289ea9a6a3e3e70" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "c6197f1da8168ac56d61807b", +"None": "c6197f1da8168ac56d61807b", +"bigquery": "c6197f1da8168ac56d61807b", +"databricks": "c6197f1da8168ac56d61807b", +"duckdb": "c6197f1da8168ac56d61807b", +"generic": "c6197f1da8168ac56d61807b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6197f1da8168ac56d61807b", +"postgres": "c6197f1da8168ac56d61807b", +"postgresql": "c6197f1da8168ac56d61807b", +"snowflake": "c6197f1da8168ac56d61807b", +"tsql": "c6197f1da8168ac56d61807b" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "e998cd8f0f74af0526b75ff0", +"None": "e998cd8f0f74af0526b75ff0", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "e998cd8f0f74af0526b75ff0", +"duckdb": "e998cd8f0f74af0526b75ff0", +"generic": "e998cd8f0f74af0526b75ff0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e998cd8f0f74af0526b75ff0", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e998cd8f0f74af0526b75ff0" +} +], +[ +"type", +{ +"DuckDB": "58b79aaeb9587eafe21bc119", +"None": "58b79aaeb9587eafe21bc119", +"bigquery": "58b79aaeb9587eafe21bc119", +"databricks": "58b79aaeb9587eafe21bc119", +"duckdb": "58b79aaeb9587eafe21bc119", +"generic": "58b79aaeb9587eafe21bc119", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "58b79aaeb9587eafe21bc119", +"postgres": "58b79aaeb9587eafe21bc119", +"postgresql": "58b79aaeb9587eafe21bc119", +"snowflake": "58b79aaeb9587eafe21bc119", +"tsql": "58b79aaeb9587eafe21bc119" +} +], +[ +"type", +{ +"DuckDB": "776e4bf23c8ef10ffaceade2", +"None": "776e4bf23c8ef10ffaceade2", +"bigquery": "776e4bf23c8ef10ffaceade2", +"databricks": "776e4bf23c8ef10ffaceade2", +"duckdb": "776e4bf23c8ef10ffaceade2", +"generic": "776e4bf23c8ef10ffaceade2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "776e4bf23c8ef10ffaceade2", +"postgres": "776e4bf23c8ef10ffaceade2", +"postgresql": "776e4bf23c8ef10ffaceade2", +"snowflake": "776e4bf23c8ef10ffaceade2", +"tsql": "776e4bf23c8ef10ffaceade2" +} +], +[ +"type", +{ +"DuckDB": "73d31c6e78cdc3b595b9c4e9", +"None": "73d31c6e78cdc3b595b9c4e9", +"bigquery": "73d31c6e78cdc3b595b9c4e9", +"databricks": "73d31c6e78cdc3b595b9c4e9", +"duckdb": "73d31c6e78cdc3b595b9c4e9", +"generic": "73d31c6e78cdc3b595b9c4e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73d31c6e78cdc3b595b9c4e9", +"postgres": "73d31c6e78cdc3b595b9c4e9", +"postgresql": "73d31c6e78cdc3b595b9c4e9", +"snowflake": "73d31c6e78cdc3b595b9c4e9", +"tsql": "73d31c6e78cdc3b595b9c4e9" +} +], +[ +"type", +{ +"DuckDB": "57922064a7dc546cbb50e42b", +"None": "57922064a7dc546cbb50e42b", +"bigquery": "57922064a7dc546cbb50e42b", +"databricks": "57922064a7dc546cbb50e42b", +"duckdb": "57922064a7dc546cbb50e42b", +"generic": "57922064a7dc546cbb50e42b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "57922064a7dc546cbb50e42b", +"postgres": "57922064a7dc546cbb50e42b", +"postgresql": "57922064a7dc546cbb50e42b", +"snowflake": "57922064a7dc546cbb50e42b", +"tsql": "57922064a7dc546cbb50e42b" +} +], +[ +"type", +{ +"DuckDB": "e408f2d8251e1675179187e6", +"None": "e408f2d8251e1675179187e6", +"bigquery": "e408f2d8251e1675179187e6", +"databricks": "e408f2d8251e1675179187e6", +"duckdb": "e408f2d8251e1675179187e6", +"generic": "e408f2d8251e1675179187e6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e408f2d8251e1675179187e6", +"postgres": "e408f2d8251e1675179187e6", +"postgresql": "e408f2d8251e1675179187e6", +"snowflake": "e408f2d8251e1675179187e6", +"tsql": "e408f2d8251e1675179187e6" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "5c528c7b4329aeb692ec6e94", +"None": "5c528c7b4329aeb692ec6e94", +"bigquery": "5c528c7b4329aeb692ec6e94", +"databricks": "5c528c7b4329aeb692ec6e94", +"duckdb": "5c528c7b4329aeb692ec6e94", +"generic": "5c528c7b4329aeb692ec6e94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c528c7b4329aeb692ec6e94", +"postgres": "5c528c7b4329aeb692ec6e94", +"postgresql": "5c528c7b4329aeb692ec6e94", +"snowflake": "5c528c7b4329aeb692ec6e94", +"tsql": "5c528c7b4329aeb692ec6e94" +} +], +[ +"type", +{ +"DuckDB": "96ecb0bd0c27167bf4e31a08", +"None": "96ecb0bd0c27167bf4e31a08", +"bigquery": "96ecb0bd0c27167bf4e31a08", +"databricks": "96ecb0bd0c27167bf4e31a08", +"duckdb": "96ecb0bd0c27167bf4e31a08", +"generic": "96ecb0bd0c27167bf4e31a08", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "96ecb0bd0c27167bf4e31a08", +"postgres": "96ecb0bd0c27167bf4e31a08", +"postgresql": "96ecb0bd0c27167bf4e31a08", +"snowflake": "96ecb0bd0c27167bf4e31a08", +"tsql": "96ecb0bd0c27167bf4e31a08" +} +], +[ +"type", +{ +"DuckDB": "b46dfa976d5ba04f95bea32b", +"None": "b46dfa976d5ba04f95bea32b", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "b46dfa976d5ba04f95bea32b", +"duckdb": "b46dfa976d5ba04f95bea32b", +"generic": "b46dfa976d5ba04f95bea32b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b46dfa976d5ba04f95bea32b", +"postgres": "34dcdfe3e3afe6140e1353e0", +"postgresql": "34dcdfe3e3afe6140e1353e0", +"snowflake": "b46dfa976d5ba04f95bea32b", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "f8262a8fca64e4491f151610", +"None": "dee6957f9182718819d5f721", +"bigquery": "c9c3ab06d6a47f6501ca2f3f", +"databricks": "47f8ea982c36ab449140ac7a", +"duckdb": "f8262a8fca64e4491f151610", +"generic": "dee6957f9182718819d5f721", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dee6957f9182718819d5f721", +"postgres": "5bc5cf0052fdc17435b19fc2", +"postgresql": "5bc5cf0052fdc17435b19fc2", +"snowflake": "f8262a8fca64e4491f151610", +"tsql": "dee6957f9182718819d5f721" +} +], +[ +"type", +{ +"DuckDB": "b694aa93969368381cb9d9bd", +"None": "b694aa93969368381cb9d9bd", +"bigquery": "df6620c838173adb34d41a29", +"databricks": "b694aa93969368381cb9d9bd", +"duckdb": "b694aa93969368381cb9d9bd", +"generic": "b694aa93969368381cb9d9bd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b694aa93969368381cb9d9bd", +"postgres": "b694aa93969368381cb9d9bd", +"postgresql": "b694aa93969368381cb9d9bd", +"snowflake": "58d5f123164e58c358315897", +"tsql": "b694aa93969368381cb9d9bd" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "2a0e1cbf59d282b31816d003", +"None": "2a0e1cbf59d282b31816d003", +"bigquery": "a49cf160806465e17f3bb015", +"databricks": "a49cf160806465e17f3bb015", +"duckdb": "2a0e1cbf59d282b31816d003", +"generic": "2a0e1cbf59d282b31816d003", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a49cf160806465e17f3bb015", +"postgres": "2a0e1cbf59d282b31816d003", +"postgresql": "2a0e1cbf59d282b31816d003", +"snowflake": "8cdd1c724939d35c854480a1", +"tsql": "2a0e1cbf59d282b31816d003" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "401e8475dd39d667a86332fd", +"None": "401e8475dd39d667a86332fd", +"bigquery": "122a82cc999a6e522d790b87", +"databricks": "e33f1ebea03ff1cb2c6e78c4", +"duckdb": "401e8475dd39d667a86332fd", +"generic": "401e8475dd39d667a86332fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "401e8475dd39d667a86332fd", +"postgres": "401e8475dd39d667a86332fd", +"postgresql": "401e8475dd39d667a86332fd", +"snowflake": "5f8d9b6f22793dc26cf2fd62", +"tsql": "0bb39b405fb474593040f307" +} +], +[ +"type", +{ +"DuckDB": "4062ec81199547543ab068fb", +"None": "4062ec81199547543ab068fb", +"bigquery": "7009fc15fd2c35608ab16caf", +"databricks": "0739784ba4c53d23afd45a6b", +"duckdb": "4062ec81199547543ab068fb", +"generic": "4062ec81199547543ab068fb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4062ec81199547543ab068fb", +"postgres": "4062ec81199547543ab068fb", +"postgresql": "4062ec81199547543ab068fb", +"snowflake": "0739784ba4c53d23afd45a6b", +"tsql": "0739784ba4c53d23afd45a6b" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "c344d36a6751b773df74ee3c", +"None": "87fed6659d6324c5ef0c6ee2", +"bigquery": "87fed6659d6324c5ef0c6ee2", +"databricks": "87fed6659d6324c5ef0c6ee2", +"duckdb": "c344d36a6751b773df74ee3c", +"generic": "87fed6659d6324c5ef0c6ee2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "87fed6659d6324c5ef0c6ee2", +"postgres": "87fed6659d6324c5ef0c6ee2", +"postgresql": "87fed6659d6324c5ef0c6ee2", +"snowflake": "87fed6659d6324c5ef0c6ee2", +"tsql": "87fed6659d6324c5ef0c6ee2" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "b2598e21f41a4625fa001e55", +"None": "b2598e21f41a4625fa001e55", +"bigquery": "b2598e21f41a4625fa001e55", +"databricks": "b2598e21f41a4625fa001e55", +"duckdb": "b2598e21f41a4625fa001e55", +"generic": "b2598e21f41a4625fa001e55", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b2598e21f41a4625fa001e55", +"postgres": "b2598e21f41a4625fa001e55", +"postgresql": "b2598e21f41a4625fa001e55", +"snowflake": "b2598e21f41a4625fa001e55", +"tsql": "b2598e21f41a4625fa001e55" +} +], +[ +"type", +{ +"DuckDB": "8a7aa7fa75696327320e0add", +"None": "8a7aa7fa75696327320e0add", +"bigquery": "e62af34c2d31bc9ab880629e", +"databricks": "e62af34c2d31bc9ab880629e", +"duckdb": "8a7aa7fa75696327320e0add", +"generic": "8a7aa7fa75696327320e0add", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e62af34c2d31bc9ab880629e", +"postgres": "8a7aa7fa75696327320e0add", +"postgresql": "8a7aa7fa75696327320e0add", +"snowflake": "8a7aa7fa75696327320e0add", +"tsql": "8a7aa7fa75696327320e0add" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "95050a5e73c2bff179a785b7", +"None": "95050a5e73c2bff179a785b7", +"bigquery": "95050a5e73c2bff179a785b7", +"databricks": "95050a5e73c2bff179a785b7", +"duckdb": "95050a5e73c2bff179a785b7", +"generic": "95050a5e73c2bff179a785b7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "95050a5e73c2bff179a785b7", +"postgres": "95050a5e73c2bff179a785b7", +"postgresql": "95050a5e73c2bff179a785b7", +"snowflake": "95050a5e73c2bff179a785b7", +"tsql": "95050a5e73c2bff179a785b7" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "314ed506f24e92300a425780", +"None": "314ed506f24e92300a425780", +"bigquery": "314ed506f24e92300a425780", +"databricks": "314ed506f24e92300a425780", +"duckdb": "314ed506f24e92300a425780", +"generic": "314ed506f24e92300a425780", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "314ed506f24e92300a425780", +"postgres": "314ed506f24e92300a425780", +"postgresql": "314ed506f24e92300a425780", +"snowflake": "314ed506f24e92300a425780", +"tsql": "314ed506f24e92300a425780" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "f8d53ecf4ba59d519bb15d38", +"None": "f8d53ecf4ba59d519bb15d38", +"bigquery": "f8d53ecf4ba59d519bb15d38", +"databricks": "f8d53ecf4ba59d519bb15d38", +"duckdb": "f8d53ecf4ba59d519bb15d38", +"generic": "f8d53ecf4ba59d519bb15d38", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f8d53ecf4ba59d519bb15d38", +"postgres": "f8d53ecf4ba59d519bb15d38", +"postgresql": "f8d53ecf4ba59d519bb15d38", +"snowflake": "f8d53ecf4ba59d519bb15d38", +"tsql": "f8d53ecf4ba59d519bb15d38" +} +], +[ +"type", +{ +"DuckDB": "d6e37cd37419bfb26e09b080", +"None": "d6e37cd37419bfb26e09b080", +"bigquery": "d6e37cd37419bfb26e09b080", +"databricks": "d6e37cd37419bfb26e09b080", +"duckdb": "d6e37cd37419bfb26e09b080", +"generic": "d6e37cd37419bfb26e09b080", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6e37cd37419bfb26e09b080", +"postgres": "d6e37cd37419bfb26e09b080", +"postgresql": "d6e37cd37419bfb26e09b080", +"snowflake": "d6e37cd37419bfb26e09b080", +"tsql": "d6e37cd37419bfb26e09b080" +} +], +[ +"type", +{ +"DuckDB": "94f6df33eccb97170eccb01b", +"None": "94f6df33eccb97170eccb01b", +"bigquery": "94f6df33eccb97170eccb01b", +"databricks": "94f6df33eccb97170eccb01b", +"duckdb": "94f6df33eccb97170eccb01b", +"generic": "94f6df33eccb97170eccb01b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "94f6df33eccb97170eccb01b", +"postgres": "94f6df33eccb97170eccb01b", +"postgresql": "94f6df33eccb97170eccb01b", +"snowflake": "94f6df33eccb97170eccb01b", +"tsql": "94f6df33eccb97170eccb01b" +} +], +[ +"type", +{ +"DuckDB": "8c0b6775b8364e21d86e6e85", +"None": "8c0b6775b8364e21d86e6e85", +"bigquery": "8c0b6775b8364e21d86e6e85", +"databricks": "8c0b6775b8364e21d86e6e85", +"duckdb": "8c0b6775b8364e21d86e6e85", +"generic": "8c0b6775b8364e21d86e6e85", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8c0b6775b8364e21d86e6e85", +"postgres": "8c0b6775b8364e21d86e6e85", +"postgresql": "8c0b6775b8364e21d86e6e85", +"snowflake": "8c0b6775b8364e21d86e6e85", +"tsql": "8c0b6775b8364e21d86e6e85" +} +], +[ +"type", +{ +"DuckDB": "2038903a8b2174e3b3525f45", +"None": "2038903a8b2174e3b3525f45", +"bigquery": "2038903a8b2174e3b3525f45", +"databricks": "2038903a8b2174e3b3525f45", +"duckdb": "2038903a8b2174e3b3525f45", +"generic": "2038903a8b2174e3b3525f45", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2038903a8b2174e3b3525f45", +"postgres": "2038903a8b2174e3b3525f45", +"postgresql": "2038903a8b2174e3b3525f45", +"snowflake": "2038903a8b2174e3b3525f45", +"tsql": "2038903a8b2174e3b3525f45" +} +], +[ +"type", +{ +"DuckDB": "57a219193ee01aa40a7a339d", +"None": "1cff449f8adc98f1234ffeec", +"bigquery": "c49aa38b33ba6204f42571c8", +"databricks": "1cff449f8adc98f1234ffeec", +"duckdb": "57a219193ee01aa40a7a339d", +"generic": "1cff449f8adc98f1234ffeec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1cff449f8adc98f1234ffeec", +"postgres": "1cff449f8adc98f1234ffeec", +"postgresql": "1cff449f8adc98f1234ffeec", +"snowflake": "bfb659237d70eae4d07a6d10", +"tsql": "1cff449f8adc98f1234ffeec" +} +], +[ +"type", +{ +"DuckDB": "e0faa139ea3b65b1356bd333", +"None": "e0faa139ea3b65b1356bd333", +"bigquery": "e0faa139ea3b65b1356bd333", +"databricks": "e0faa139ea3b65b1356bd333", +"duckdb": "e0faa139ea3b65b1356bd333", +"generic": "e0faa139ea3b65b1356bd333", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e0faa139ea3b65b1356bd333", +"postgres": "e0faa139ea3b65b1356bd333", +"postgresql": "e0faa139ea3b65b1356bd333", +"snowflake": "e0faa139ea3b65b1356bd333", +"tsql": "e0faa139ea3b65b1356bd333" +} +], +[ +"type", +{ +"DuckDB": "42d4a23d9a52e79d0c3458d3", +"None": "a3acd7ba35e81a800f967bd3", +"bigquery": "3c0719ca5c83e0a20d0b0b42", +"databricks": "a3acd7ba35e81a800f967bd3", +"duckdb": "42d4a23d9a52e79d0c3458d3", +"generic": "a3acd7ba35e81a800f967bd3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a3acd7ba35e81a800f967bd3", +"postgres": "a3acd7ba35e81a800f967bd3", +"postgresql": "a3acd7ba35e81a800f967bd3", +"snowflake": "a3acd7ba35e81a800f967bd3", +"tsql": "a3acd7ba35e81a800f967bd3" +} +], +[ +"type", +{ +"DuckDB": "2795ccedbfd3fa0bed5fdfcd", +"None": "2795ccedbfd3fa0bed5fdfcd", +"bigquery": "2795ccedbfd3fa0bed5fdfcd", +"databricks": "2795ccedbfd3fa0bed5fdfcd", +"duckdb": "2795ccedbfd3fa0bed5fdfcd", +"generic": "2795ccedbfd3fa0bed5fdfcd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2795ccedbfd3fa0bed5fdfcd", +"postgres": "2795ccedbfd3fa0bed5fdfcd", +"postgresql": "2795ccedbfd3fa0bed5fdfcd", +"snowflake": "2795ccedbfd3fa0bed5fdfcd", +"tsql": "2795ccedbfd3fa0bed5fdfcd" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "4a014523269fae92f3ad421a", +"None": "4a014523269fae92f3ad421a", +"bigquery": "4a014523269fae92f3ad421a", +"databricks": "4a014523269fae92f3ad421a", +"duckdb": "4a014523269fae92f3ad421a", +"generic": "4a014523269fae92f3ad421a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4a014523269fae92f3ad421a", +"postgres": "4a014523269fae92f3ad421a", +"postgresql": "4a014523269fae92f3ad421a", +"snowflake": "4a014523269fae92f3ad421a", +"tsql": "4a014523269fae92f3ad421a" +} +], +[ +"type", +{ +"DuckDB": "684469f7d4198e43e63072ce", +"None": "684469f7d4198e43e63072ce", +"bigquery": "684469f7d4198e43e63072ce", +"databricks": "684469f7d4198e43e63072ce", +"duckdb": "684469f7d4198e43e63072ce", +"generic": "684469f7d4198e43e63072ce", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "684469f7d4198e43e63072ce", +"postgres": "684469f7d4198e43e63072ce", +"postgresql": "684469f7d4198e43e63072ce", +"snowflake": "684469f7d4198e43e63072ce", +"tsql": "684469f7d4198e43e63072ce" +} +], +[ +"type", +{ +"DuckDB": "e998cd8f0f74af0526b75ff0", +"None": "e998cd8f0f74af0526b75ff0", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "e998cd8f0f74af0526b75ff0", +"duckdb": "e998cd8f0f74af0526b75ff0", +"generic": "e998cd8f0f74af0526b75ff0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e998cd8f0f74af0526b75ff0", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e998cd8f0f74af0526b75ff0" +} +], +[ +"type", +{ +"DuckDB": "110327dc45eac0e8d54a96dc", +"None": "110327dc45eac0e8d54a96dc", +"bigquery": "1a804f317a3e9a0016925b04", +"databricks": "1a804f317a3e9a0016925b04", +"duckdb": "110327dc45eac0e8d54a96dc", +"generic": "110327dc45eac0e8d54a96dc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1a804f317a3e9a0016925b04", +"postgres": "110327dc45eac0e8d54a96dc", +"postgresql": "110327dc45eac0e8d54a96dc", +"snowflake": "110327dc45eac0e8d54a96dc", +"tsql": "110327dc45eac0e8d54a96dc" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "4d02ddd0ab80d0d46bf48a5f", +"None": "4d02ddd0ab80d0d46bf48a5f", +"bigquery": "4d02ddd0ab80d0d46bf48a5f", +"databricks": "4d02ddd0ab80d0d46bf48a5f", +"duckdb": "4d02ddd0ab80d0d46bf48a5f", +"generic": "4d02ddd0ab80d0d46bf48a5f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4d02ddd0ab80d0d46bf48a5f", +"postgres": "4d02ddd0ab80d0d46bf48a5f", +"postgresql": "4d02ddd0ab80d0d46bf48a5f", +"snowflake": "4d02ddd0ab80d0d46bf48a5f", +"tsql": "4d02ddd0ab80d0d46bf48a5f" +} +], +[ +"type", +{ +"DuckDB": "0ca1be8f7d61ca627e620450", +"None": "0ca1be8f7d61ca627e620450", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "0ca1be8f7d61ca627e620450", +"duckdb": "0ca1be8f7d61ca627e620450", +"generic": "0ca1be8f7d61ca627e620450", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0ca1be8f7d61ca627e620450", +"postgres": "0ca1be8f7d61ca627e620450", +"postgresql": "0ca1be8f7d61ca627e620450", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "0ca1be8f7d61ca627e620450" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "d5af64f521c46f0f5c6d1ad0", +"None": "d5af64f521c46f0f5c6d1ad0", +"bigquery": "d5af64f521c46f0f5c6d1ad0", +"databricks": "d5af64f521c46f0f5c6d1ad0", +"duckdb": "d5af64f521c46f0f5c6d1ad0", +"generic": "d5af64f521c46f0f5c6d1ad0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d5af64f521c46f0f5c6d1ad0", +"postgres": "d5af64f521c46f0f5c6d1ad0", +"postgresql": "d5af64f521c46f0f5c6d1ad0", +"snowflake": "d5af64f521c46f0f5c6d1ad0", +"tsql": "d5af64f521c46f0f5c6d1ad0" +} +], +[ +"type", +{ +"DuckDB": "38e28682c0f47e13e642ab1c", +"None": "38e28682c0f47e13e642ab1c", +"bigquery": "96315ac85cc1ab3d0ea56ca2", +"databricks": "6e4f718d80b641c205a88635", +"duckdb": "38e28682c0f47e13e642ab1c", +"generic": "38e28682c0f47e13e642ab1c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "25f9faed41d5d23ca3fa8175", +"postgres": "38e28682c0f47e13e642ab1c", +"postgresql": "38e28682c0f47e13e642ab1c", +"snowflake": "41cf14d12db9a99ffd9a4514", +"tsql": "38e28682c0f47e13e642ab1c" +} +], +[ +"type", +{ +"DuckDB": "0a735e1254c48785122ee5a3", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "0a735e1254c48785122ee5a3", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "c16b4b4f382469fb186a9022", +"None": "c16b4b4f382469fb186a9022", +"bigquery": "c16b4b4f382469fb186a9022", +"databricks": "c16b4b4f382469fb186a9022", +"duckdb": "c16b4b4f382469fb186a9022", +"generic": "c16b4b4f382469fb186a9022", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c16b4b4f382469fb186a9022", +"postgres": "c16b4b4f382469fb186a9022", +"postgresql": "c16b4b4f382469fb186a9022", +"snowflake": "c16b4b4f382469fb186a9022", +"tsql": "c16b4b4f382469fb186a9022" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "9e4a50a8aab648a9737f7d0c", +"None": "9e4a50a8aab648a9737f7d0c", +"bigquery": "d9a4e245156e760303573500", +"databricks": "9e4a50a8aab648a9737f7d0c", +"duckdb": "9e4a50a8aab648a9737f7d0c", +"generic": "9e4a50a8aab648a9737f7d0c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9e4a50a8aab648a9737f7d0c", +"postgres": "36d29258901a4d8c296baa68", +"postgresql": "36d29258901a4d8c296baa68", +"snowflake": "9e4a50a8aab648a9737f7d0c", +"tsql": "355e032cc5c0f24695836b3d" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "e8605878c6fffc044556a858", +"None": "e8605878c6fffc044556a858", +"bigquery": "e8605878c6fffc044556a858", +"databricks": "e8605878c6fffc044556a858", +"duckdb": "e8605878c6fffc044556a858", +"generic": "e8605878c6fffc044556a858", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8605878c6fffc044556a858", +"postgres": "e8605878c6fffc044556a858", +"postgresql": "e8605878c6fffc044556a858", +"snowflake": "e8605878c6fffc044556a858", +"tsql": "e8605878c6fffc044556a858" +} +], +[ +"type", +{ +"DuckDB": "e8ef65ce7616ceeafffe41f6", +"None": "e8ef65ce7616ceeafffe41f6", +"bigquery": "e8ef65ce7616ceeafffe41f6", +"databricks": "e8ef65ce7616ceeafffe41f6", +"duckdb": "e8ef65ce7616ceeafffe41f6", +"generic": "e8ef65ce7616ceeafffe41f6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8ef65ce7616ceeafffe41f6", +"postgres": "e8ef65ce7616ceeafffe41f6", +"postgresql": "e8ef65ce7616ceeafffe41f6", +"snowflake": "e8ef65ce7616ceeafffe41f6", +"tsql": "e8ef65ce7616ceeafffe41f6" +} +], +[ +"type", +{ +"DuckDB": "77ffdb80029bd6e2cca768ad", +"None": "77ffdb80029bd6e2cca768ad", +"bigquery": "77ffdb80029bd6e2cca768ad", +"databricks": "77ffdb80029bd6e2cca768ad", +"duckdb": "77ffdb80029bd6e2cca768ad", +"generic": "77ffdb80029bd6e2cca768ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "77ffdb80029bd6e2cca768ad", +"postgres": "77ffdb80029bd6e2cca768ad", +"postgresql": "77ffdb80029bd6e2cca768ad", +"snowflake": "77ffdb80029bd6e2cca768ad", +"tsql": "77ffdb80029bd6e2cca768ad" +} +], +[ +"type", +{ +"DuckDB": "c4346d0257336219f1d26890", +"None": "c4346d0257336219f1d26890", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "c4346d0257336219f1d26890", +"duckdb": "c4346d0257336219f1d26890", +"generic": "c4346d0257336219f1d26890", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4346d0257336219f1d26890", +"postgres": "c4346d0257336219f1d26890", +"postgresql": "c4346d0257336219f1d26890", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "c4346d0257336219f1d26890" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "3e611643f0beab4953c4e09b", +"None": "3e611643f0beab4953c4e09b", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "3e611643f0beab4953c4e09b", +"duckdb": "3e611643f0beab4953c4e09b", +"generic": "3e611643f0beab4953c4e09b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3e611643f0beab4953c4e09b", +"postgres": "3e611643f0beab4953c4e09b", +"postgresql": "3e611643f0beab4953c4e09b", +"snowflake": "3e611643f0beab4953c4e09b", +"tsql": "3e611643f0beab4953c4e09b" +} +], +[ +"type", +{ +"DuckDB": "776e4bf23c8ef10ffaceade2", +"None": "776e4bf23c8ef10ffaceade2", +"bigquery": "776e4bf23c8ef10ffaceade2", +"databricks": "776e4bf23c8ef10ffaceade2", +"duckdb": "776e4bf23c8ef10ffaceade2", +"generic": "776e4bf23c8ef10ffaceade2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "776e4bf23c8ef10ffaceade2", +"postgres": "776e4bf23c8ef10ffaceade2", +"postgresql": "776e4bf23c8ef10ffaceade2", +"snowflake": "776e4bf23c8ef10ffaceade2", +"tsql": "776e4bf23c8ef10ffaceade2" +} +], +[ +"type", +{ +"DuckDB": "3915f3f73328f0838497b2d5", +"None": "3915f3f73328f0838497b2d5", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "3915f3f73328f0838497b2d5", +"duckdb": "3915f3f73328f0838497b2d5", +"generic": "3915f3f73328f0838497b2d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3915f3f73328f0838497b2d5", +"postgres": "3915f3f73328f0838497b2d5", +"postgresql": "3915f3f73328f0838497b2d5", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3915f3f73328f0838497b2d5" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "eed0645c1e86fa1a6e35ccaa", +"None": "eed0645c1e86fa1a6e35ccaa", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "eed0645c1e86fa1a6e35ccaa", +"duckdb": "eed0645c1e86fa1a6e35ccaa", +"generic": "eed0645c1e86fa1a6e35ccaa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eed0645c1e86fa1a6e35ccaa", +"postgres": "eed0645c1e86fa1a6e35ccaa", +"postgresql": "eed0645c1e86fa1a6e35ccaa", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "eed0645c1e86fa1a6e35ccaa" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "962ea05bab624c1f8c259a65", +"None": "962ea05bab624c1f8c259a65", +"bigquery": "962ea05bab624c1f8c259a65", +"databricks": "962ea05bab624c1f8c259a65", +"duckdb": "962ea05bab624c1f8c259a65", +"generic": "962ea05bab624c1f8c259a65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "962ea05bab624c1f8c259a65", +"postgres": "962ea05bab624c1f8c259a65", +"postgresql": "962ea05bab624c1f8c259a65", +"snowflake": "962ea05bab624c1f8c259a65", +"tsql": "962ea05bab624c1f8c259a65" +} +], +[ +"type", +{ +"DuckDB": "207acc46dcc150502155f4a3", +"None": "d638c2852078bf3facb9dc9e", +"bigquery": "285a15e7c9efb8c0d32d6070", +"databricks": "d638c2852078bf3facb9dc9e", +"duckdb": "207acc46dcc150502155f4a3", +"generic": "d638c2852078bf3facb9dc9e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "6c49aec265f259dd95b5310e", +"postgresql": "6c49aec265f259dd95b5310e", +"snowflake": "ef2c05747b795f92c0b523ee", +"tsql": "2954e90d1d02f8a24fb095ea" +} +], +[ +"type", +{ +"DuckDB": "9d006c5299feb04426ce31ac", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "9d006c5299feb04426ce31ac", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "6a726882dbf443e39240fc33", +"None": "6a726882dbf443e39240fc33", +"bigquery": "6a726882dbf443e39240fc33", +"databricks": "6a726882dbf443e39240fc33", +"duckdb": "6a726882dbf443e39240fc33", +"generic": "6a726882dbf443e39240fc33", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a726882dbf443e39240fc33", +"postgres": "6a726882dbf443e39240fc33", +"postgresql": "6a726882dbf443e39240fc33", +"snowflake": "6a726882dbf443e39240fc33", +"tsql": "6a726882dbf443e39240fc33" +} +], +[ +"type", +{ +"DuckDB": "c4860fb394ecca5e4515f1c9", +"None": "c4860fb394ecca5e4515f1c9", +"bigquery": "c4860fb394ecca5e4515f1c9", +"databricks": "c4860fb394ecca5e4515f1c9", +"duckdb": "c4860fb394ecca5e4515f1c9", +"generic": "c4860fb394ecca5e4515f1c9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4860fb394ecca5e4515f1c9", +"postgres": "c4860fb394ecca5e4515f1c9", +"postgresql": "c4860fb394ecca5e4515f1c9", +"snowflake": "c4860fb394ecca5e4515f1c9", +"tsql": "c4860fb394ecca5e4515f1c9" +} +], +[ +"type", +{ +"DuckDB": "363159f251e264604338e690", +"None": "363159f251e264604338e690", +"bigquery": "363159f251e264604338e690", +"databricks": "363159f251e264604338e690", +"duckdb": "363159f251e264604338e690", +"generic": "363159f251e264604338e690", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "363159f251e264604338e690", +"postgres": "363159f251e264604338e690", +"postgresql": "363159f251e264604338e690", +"snowflake": "363159f251e264604338e690", +"tsql": "363159f251e264604338e690" +} +], +[ +"type", +{ +"DuckDB": "fe1d4d12e4b12d78f4ce545d", +"None": "fe1d4d12e4b12d78f4ce545d", +"bigquery": "fe1d4d12e4b12d78f4ce545d", +"databricks": "fe1d4d12e4b12d78f4ce545d", +"duckdb": "fe1d4d12e4b12d78f4ce545d", +"generic": "fe1d4d12e4b12d78f4ce545d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe1d4d12e4b12d78f4ce545d", +"postgres": "fe1d4d12e4b12d78f4ce545d", +"postgresql": "fe1d4d12e4b12d78f4ce545d", +"snowflake": "fe1d4d12e4b12d78f4ce545d", +"tsql": "fe1d4d12e4b12d78f4ce545d" +} +], +[ +"type", +{ +"DuckDB": "5829c1427d562e21c1ed4dfe", +"None": "5829c1427d562e21c1ed4dfe", +"bigquery": "5829c1427d562e21c1ed4dfe", +"databricks": "5829c1427d562e21c1ed4dfe", +"duckdb": "5829c1427d562e21c1ed4dfe", +"generic": "5829c1427d562e21c1ed4dfe", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5829c1427d562e21c1ed4dfe", +"postgres": "5829c1427d562e21c1ed4dfe", +"postgresql": "5829c1427d562e21c1ed4dfe", +"snowflake": "5829c1427d562e21c1ed4dfe", +"tsql": "5829c1427d562e21c1ed4dfe" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "e3ba57c3380e7ca53675e905", +"None": "85dad253f82f475892a7b35b", +"bigquery": "85dad253f82f475892a7b35b", +"databricks": "534433bc99c8d49f592ba435", +"duckdb": "e3ba57c3380e7ca53675e905", +"generic": "85dad253f82f475892a7b35b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dd1a4411040373d9cd82f86", +"postgres": "463c3432687f6ea95aa60355", +"postgresql": "463c3432687f6ea95aa60355", +"snowflake": "0161e12cd4d2efd1b5557348", +"tsql": "87673dc143bcd33d8688dc34" +} +], +[ +"type", +{ +"DuckDB": "1fe8a2d5a41383bd8e502808", +"None": "1fe8a2d5a41383bd8e502808", +"bigquery": "70f3d045595fbd43219efb36", +"databricks": "70f3d045595fbd43219efb36", +"duckdb": "1fe8a2d5a41383bd8e502808", +"generic": "1fe8a2d5a41383bd8e502808", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70f3d045595fbd43219efb36", +"postgres": "1fe8a2d5a41383bd8e502808", +"postgresql": "1fe8a2d5a41383bd8e502808", +"snowflake": "1fe8a2d5a41383bd8e502808", +"tsql": "1fe8a2d5a41383bd8e502808" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "1c58068460e5c94bc2f83d53", +"None": "1c58068460e5c94bc2f83d53", +"bigquery": "1c58068460e5c94bc2f83d53", +"databricks": "1c58068460e5c94bc2f83d53", +"duckdb": "1c58068460e5c94bc2f83d53", +"generic": "1c58068460e5c94bc2f83d53", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1c58068460e5c94bc2f83d53", +"postgres": "1c58068460e5c94bc2f83d53", +"postgresql": "1c58068460e5c94bc2f83d53", +"snowflake": "1c58068460e5c94bc2f83d53", +"tsql": "1c58068460e5c94bc2f83d53" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "26e762643db86e7646e57cba", +"None": "26e762643db86e7646e57cba", +"bigquery": "26e762643db86e7646e57cba", +"databricks": "26e762643db86e7646e57cba", +"duckdb": "26e762643db86e7646e57cba", +"generic": "26e762643db86e7646e57cba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "26e762643db86e7646e57cba", +"postgres": "26e762643db86e7646e57cba", +"postgresql": "26e762643db86e7646e57cba", +"snowflake": "26e762643db86e7646e57cba", +"tsql": "26e762643db86e7646e57cba" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "243652b8dd9a0f3ac3501ed4", +"None": "f74ed75cd28860b40bc2c2e4", +"bigquery": "f74ed75cd28860b40bc2c2e4", +"databricks": "7c0baf18eb7c47a1c83b71c3", +"duckdb": "243652b8dd9a0f3ac3501ed4", +"generic": "f74ed75cd28860b40bc2c2e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "240398aee89da87a2f8274c9", +"postgresql": "240398aee89da87a2f8274c9", +"snowflake": "7422d281eba97abeec05a617", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "72bd5e38b32893397eb51579", +"None": "1bdf5908ca249b2331a0427c", +"bigquery": "1bdf5908ca249b2331a0427c", +"databricks": "1bdf5908ca249b2331a0427c", +"duckdb": "72bd5e38b32893397eb51579", +"generic": "1bdf5908ca249b2331a0427c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1bdf5908ca249b2331a0427c", +"postgres": "1bdf5908ca249b2331a0427c", +"postgresql": "1bdf5908ca249b2331a0427c", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "1bdf5908ca249b2331a0427c" +} +], +[ +"type", +{ +"DuckDB": "d725148d39793a0cbb9a6013", +"None": "d725148d39793a0cbb9a6013", +"bigquery": "d725148d39793a0cbb9a6013", +"databricks": "d725148d39793a0cbb9a6013", +"duckdb": "d725148d39793a0cbb9a6013", +"generic": "d725148d39793a0cbb9a6013", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d725148d39793a0cbb9a6013", +"postgres": "d725148d39793a0cbb9a6013", +"postgresql": "d725148d39793a0cbb9a6013", +"snowflake": "d725148d39793a0cbb9a6013", +"tsql": "d725148d39793a0cbb9a6013" +} +], +[ +"type", +{ +"DuckDB": "fe0bedb7dda0e9247a51fdee", +"None": "fe0bedb7dda0e9247a51fdee", +"bigquery": "fe0bedb7dda0e9247a51fdee", +"databricks": "fe0bedb7dda0e9247a51fdee", +"duckdb": "fe0bedb7dda0e9247a51fdee", +"generic": "fe0bedb7dda0e9247a51fdee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe0bedb7dda0e9247a51fdee", +"postgres": "fe0bedb7dda0e9247a51fdee", +"postgresql": "fe0bedb7dda0e9247a51fdee", +"snowflake": "fe0bedb7dda0e9247a51fdee", +"tsql": "fe0bedb7dda0e9247a51fdee" +} +], +[ +"type", +{ +"DuckDB": "2c44dc35fd1ef8701101802e", +"None": "2c44dc35fd1ef8701101802e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "2c44dc35fd1ef8701101802e", +"duckdb": "2c44dc35fd1ef8701101802e", +"generic": "2c44dc35fd1ef8701101802e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c44dc35fd1ef8701101802e", +"postgres": "2c44dc35fd1ef8701101802e", +"postgresql": "2c44dc35fd1ef8701101802e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "2c44dc35fd1ef8701101802e" +} +], +[ +"type", +{ +"DuckDB": "c4346d0257336219f1d26890", +"None": "c4346d0257336219f1d26890", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "c4346d0257336219f1d26890", +"duckdb": "c4346d0257336219f1d26890", +"generic": "c4346d0257336219f1d26890", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4346d0257336219f1d26890", +"postgres": "c4346d0257336219f1d26890", +"postgresql": "c4346d0257336219f1d26890", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "c4346d0257336219f1d26890" +} +], +[ +"type", +{ +"DuckDB": "73ed909ac0c80ba48ae6d17c", +"None": "73ed909ac0c80ba48ae6d17c", +"bigquery": "73ed909ac0c80ba48ae6d17c", +"databricks": "73ed909ac0c80ba48ae6d17c", +"duckdb": "73ed909ac0c80ba48ae6d17c", +"generic": "73ed909ac0c80ba48ae6d17c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73ed909ac0c80ba48ae6d17c", +"postgres": "73ed909ac0c80ba48ae6d17c", +"postgresql": "73ed909ac0c80ba48ae6d17c", +"snowflake": "73ed909ac0c80ba48ae6d17c", +"tsql": "73ed909ac0c80ba48ae6d17c" +} +], +[ +"type", +{ +"DuckDB": "445eba49a8d5ccbd6775e1b6", +"None": "445eba49a8d5ccbd6775e1b6", +"bigquery": "445eba49a8d5ccbd6775e1b6", +"databricks": "445eba49a8d5ccbd6775e1b6", +"duckdb": "445eba49a8d5ccbd6775e1b6", +"generic": "445eba49a8d5ccbd6775e1b6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "445eba49a8d5ccbd6775e1b6", +"postgres": "445eba49a8d5ccbd6775e1b6", +"postgresql": "445eba49a8d5ccbd6775e1b6", +"snowflake": "445eba49a8d5ccbd6775e1b6", +"tsql": "445eba49a8d5ccbd6775e1b6" +} +], +[ +"type", +{ +"DuckDB": "097487e169d70127caa10537", +"None": "097487e169d70127caa10537", +"bigquery": "097487e169d70127caa10537", +"databricks": "097487e169d70127caa10537", +"duckdb": "097487e169d70127caa10537", +"generic": "097487e169d70127caa10537", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "097487e169d70127caa10537", +"postgres": "097487e169d70127caa10537", +"postgresql": "097487e169d70127caa10537", +"snowflake": "097487e169d70127caa10537", +"tsql": "097487e169d70127caa10537" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "5aa51d84ac5e6c7a9306f348", +"bigquery": "5aa51d84ac5e6c7a9306f348", +"databricks": "5aa51d84ac5e6c7a9306f348", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "5aa51d84ac5e6c7a9306f348", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "383310937b9286f8d0206ea4", +"None": "383310937b9286f8d0206ea4", +"bigquery": "383310937b9286f8d0206ea4", +"databricks": "383310937b9286f8d0206ea4", +"duckdb": "383310937b9286f8d0206ea4", +"generic": "383310937b9286f8d0206ea4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "383310937b9286f8d0206ea4", +"postgres": "383310937b9286f8d0206ea4", +"postgresql": "383310937b9286f8d0206ea4", +"snowflake": "383310937b9286f8d0206ea4", +"tsql": "383310937b9286f8d0206ea4" +} +], +[ +"type", +{ +"DuckDB": "3efe8bb57c98fcbe74096b3c", +"None": "3efe8bb57c98fcbe74096b3c", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "3efe8bb57c98fcbe74096b3c", +"generic": "3efe8bb57c98fcbe74096b3c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "3efe8bb57c98fcbe74096b3c" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "bbb97828cd28c64282709796", +"None": "bbb97828cd28c64282709796", +"bigquery": "bbb97828cd28c64282709796", +"databricks": "bbb97828cd28c64282709796", +"duckdb": "bbb97828cd28c64282709796", +"generic": "bbb97828cd28c64282709796", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bbb97828cd28c64282709796", +"postgres": "bbb97828cd28c64282709796", +"postgresql": "bbb97828cd28c64282709796", +"snowflake": "bbb97828cd28c64282709796", +"tsql": "bbb97828cd28c64282709796" +} +], +[ +"type", +{ +"DuckDB": "33edbb37fe70dd760e97e937", +"None": "33edbb37fe70dd760e97e937", +"bigquery": "33edbb37fe70dd760e97e937", +"databricks": "33edbb37fe70dd760e97e937", +"duckdb": "33edbb37fe70dd760e97e937", +"generic": "33edbb37fe70dd760e97e937", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "33edbb37fe70dd760e97e937", +"postgres": "33edbb37fe70dd760e97e937", +"postgresql": "33edbb37fe70dd760e97e937", +"snowflake": "33edbb37fe70dd760e97e937", +"tsql": "33edbb37fe70dd760e97e937" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "638ca9212b62ada31a448c62", +"None": "1fec266134381c760a254d16", +"bigquery": "1fec266134381c760a254d16", +"databricks": "1fec266134381c760a254d16", +"duckdb": "638ca9212b62ada31a448c62", +"generic": "1fec266134381c760a254d16", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fec266134381c760a254d16", +"postgres": "1fec266134381c760a254d16", +"postgresql": "1fec266134381c760a254d16", +"snowflake": "1fec266134381c760a254d16", +"tsql": "1fec266134381c760a254d16" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "0a735e1254c48785122ee5a3", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "0a735e1254c48785122ee5a3", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "38742651e9a5007a4262fae8", +"None": "38742651e9a5007a4262fae8", +"bigquery": "38742651e9a5007a4262fae8", +"databricks": "38742651e9a5007a4262fae8", +"duckdb": "38742651e9a5007a4262fae8", +"generic": "38742651e9a5007a4262fae8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38742651e9a5007a4262fae8", +"postgres": "38742651e9a5007a4262fae8", +"postgresql": "38742651e9a5007a4262fae8", +"snowflake": "38742651e9a5007a4262fae8", +"tsql": "38742651e9a5007a4262fae8" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "cdf6c5188287878061490c2a", +"None": "cdf6c5188287878061490c2a", +"bigquery": "8b225d02320fa30e4a4e6cb1", +"databricks": "f6e9bda6dd824d392effea47", +"duckdb": "cdf6c5188287878061490c2a", +"generic": "cdf6c5188287878061490c2a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5598594492668be60871af14", +"postgres": "cdf6c5188287878061490c2a", +"postgresql": "cdf6c5188287878061490c2a", +"snowflake": "5c643769adf301c8da08643f", +"tsql": "cdf6c5188287878061490c2a" +} +], +[ +"type", +{ +"DuckDB": "e7455ff80a579a99488df79b", +"None": "e7455ff80a579a99488df79b", +"bigquery": "e7455ff80a579a99488df79b", +"databricks": "e7455ff80a579a99488df79b", +"duckdb": "e7455ff80a579a99488df79b", +"generic": "e7455ff80a579a99488df79b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7455ff80a579a99488df79b", +"postgres": "e7455ff80a579a99488df79b", +"postgresql": "e7455ff80a579a99488df79b", +"snowflake": "e7455ff80a579a99488df79b", +"tsql": "e7455ff80a579a99488df79b" +} +], +[ +"type", +{ +"DuckDB": "2c6b371b030464f525066893", +"None": "2c6b371b030464f525066893", +"bigquery": "2c6b371b030464f525066893", +"databricks": "2c6b371b030464f525066893", +"duckdb": "2c6b371b030464f525066893", +"generic": "2c6b371b030464f525066893", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c6b371b030464f525066893", +"postgres": "2c6b371b030464f525066893", +"postgresql": "2c6b371b030464f525066893", +"snowflake": "2c6b371b030464f525066893", +"tsql": "2c6b371b030464f525066893" +} +], +[ +"type", +{ +"DuckDB": "881106a4a0d2de0ff7c56373", +"None": "881106a4a0d2de0ff7c56373", +"bigquery": "881106a4a0d2de0ff7c56373", +"databricks": "881106a4a0d2de0ff7c56373", +"duckdb": "881106a4a0d2de0ff7c56373", +"generic": "881106a4a0d2de0ff7c56373", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "881106a4a0d2de0ff7c56373", +"postgres": "881106a4a0d2de0ff7c56373", +"postgresql": "881106a4a0d2de0ff7c56373", +"snowflake": "881106a4a0d2de0ff7c56373", +"tsql": "881106a4a0d2de0ff7c56373" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "07aa3fe366bb8a3f270ea3d3", +"None": "07aa3fe366bb8a3f270ea3d3", +"bigquery": "07aa3fe366bb8a3f270ea3d3", +"databricks": "07aa3fe366bb8a3f270ea3d3", +"duckdb": "07aa3fe366bb8a3f270ea3d3", +"generic": "07aa3fe366bb8a3f270ea3d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "07aa3fe366bb8a3f270ea3d3", +"postgres": "07aa3fe366bb8a3f270ea3d3", +"postgresql": "07aa3fe366bb8a3f270ea3d3", +"snowflake": "07aa3fe366bb8a3f270ea3d3", +"tsql": "07aa3fe366bb8a3f270ea3d3" +} +], +[ +"type", +{ +"DuckDB": "d47776cdb5b03cf4b1e7b63c", +"None": "d47776cdb5b03cf4b1e7b63c", +"bigquery": "d47776cdb5b03cf4b1e7b63c", +"databricks": "d47776cdb5b03cf4b1e7b63c", +"duckdb": "d47776cdb5b03cf4b1e7b63c", +"generic": "d47776cdb5b03cf4b1e7b63c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d47776cdb5b03cf4b1e7b63c", +"postgres": "d47776cdb5b03cf4b1e7b63c", +"postgresql": "d47776cdb5b03cf4b1e7b63c", +"snowflake": "d47776cdb5b03cf4b1e7b63c", +"tsql": "d47776cdb5b03cf4b1e7b63c" +} +], +[ +"type", +{ +"DuckDB": "eed0645c1e86fa1a6e35ccaa", +"None": "eed0645c1e86fa1a6e35ccaa", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "eed0645c1e86fa1a6e35ccaa", +"duckdb": "eed0645c1e86fa1a6e35ccaa", +"generic": "eed0645c1e86fa1a6e35ccaa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eed0645c1e86fa1a6e35ccaa", +"postgres": "eed0645c1e86fa1a6e35ccaa", +"postgresql": "eed0645c1e86fa1a6e35ccaa", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "eed0645c1e86fa1a6e35ccaa" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "3959952046272deb40567543", +"None": "3959952046272deb40567543", +"bigquery": "3959952046272deb40567543", +"databricks": "3959952046272deb40567543", +"duckdb": "3959952046272deb40567543", +"generic": "3959952046272deb40567543", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3959952046272deb40567543", +"postgres": "3959952046272deb40567543", +"postgresql": "3959952046272deb40567543", +"snowflake": "3959952046272deb40567543", +"tsql": "3959952046272deb40567543" +} +], +[ +"type", +{ +"DuckDB": "c426d93641829fa3d8f49b8d", +"None": "c426d93641829fa3d8f49b8d", +"bigquery": "c426d93641829fa3d8f49b8d", +"databricks": "c426d93641829fa3d8f49b8d", +"duckdb": "c426d93641829fa3d8f49b8d", +"generic": "c426d93641829fa3d8f49b8d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c426d93641829fa3d8f49b8d", +"postgres": "c426d93641829fa3d8f49b8d", +"postgresql": "c426d93641829fa3d8f49b8d", +"snowflake": "c426d93641829fa3d8f49b8d", +"tsql": "c426d93641829fa3d8f49b8d" +} +], +[ +"type", +{ +"DuckDB": "da33de41a707d0e067e415fd", +"None": "da33de41a707d0e067e415fd", +"bigquery": "da33de41a707d0e067e415fd", +"databricks": "da33de41a707d0e067e415fd", +"duckdb": "da33de41a707d0e067e415fd", +"generic": "da33de41a707d0e067e415fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "da33de41a707d0e067e415fd", +"postgres": "da33de41a707d0e067e415fd", +"postgresql": "da33de41a707d0e067e415fd", +"snowflake": "da33de41a707d0e067e415fd", +"tsql": "da33de41a707d0e067e415fd" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "be15dde1d6e0b4bb3f6da66f", +"None": "be15dde1d6e0b4bb3f6da66f", +"bigquery": "be15dde1d6e0b4bb3f6da66f", +"databricks": "be15dde1d6e0b4bb3f6da66f", +"duckdb": "be15dde1d6e0b4bb3f6da66f", +"generic": "be15dde1d6e0b4bb3f6da66f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "be15dde1d6e0b4bb3f6da66f", +"postgres": "be15dde1d6e0b4bb3f6da66f", +"postgresql": "be15dde1d6e0b4bb3f6da66f", +"snowflake": "be15dde1d6e0b4bb3f6da66f", +"tsql": "be15dde1d6e0b4bb3f6da66f" +} +], +[ +"type", +{ +"DuckDB": "4062ec81199547543ab068fb", +"None": "4062ec81199547543ab068fb", +"bigquery": "7009fc15fd2c35608ab16caf", +"databricks": "0739784ba4c53d23afd45a6b", +"duckdb": "4062ec81199547543ab068fb", +"generic": "4062ec81199547543ab068fb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4062ec81199547543ab068fb", +"postgres": "4062ec81199547543ab068fb", +"postgresql": "4062ec81199547543ab068fb", +"snowflake": "0739784ba4c53d23afd45a6b", +"tsql": "0739784ba4c53d23afd45a6b" +} +], +[ +"type", +{ +"DuckDB": "73151c1398b30e5c5d18f4ce", +"None": "73151c1398b30e5c5d18f4ce", +"bigquery": "73151c1398b30e5c5d18f4ce", +"databricks": "73151c1398b30e5c5d18f4ce", +"duckdb": "73151c1398b30e5c5d18f4ce", +"generic": "73151c1398b30e5c5d18f4ce", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73151c1398b30e5c5d18f4ce", +"postgres": "73151c1398b30e5c5d18f4ce", +"postgresql": "73151c1398b30e5c5d18f4ce", +"snowflake": "73151c1398b30e5c5d18f4ce", +"tsql": "73151c1398b30e5c5d18f4ce" +} +], +[ +"type", +{ +"DuckDB": "e3d9a113277b9c69432b32d1", +"None": "e3d9a113277b9c69432b32d1", +"bigquery": "e3d9a113277b9c69432b32d1", +"databricks": "4cda99191af9adfbe91e70f6", +"duckdb": "e3d9a113277b9c69432b32d1", +"generic": "e3d9a113277b9c69432b32d1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4cda99191af9adfbe91e70f6", +"postgres": "e3d9a113277b9c69432b32d1", +"postgresql": "e3d9a113277b9c69432b32d1", +"snowflake": "e3d9a113277b9c69432b32d1", +"tsql": "e3d9a113277b9c69432b32d1" +} +], +[ +"type", +{ +"DuckDB": "f6c3d80bfee0ec0b286b82b5", +"None": "b137c998bdaa2555d912e8fa", +"bigquery": "b137c998bdaa2555d912e8fa", +"databricks": "b137c998bdaa2555d912e8fa", +"duckdb": "f6c3d80bfee0ec0b286b82b5", +"generic": "b137c998bdaa2555d912e8fa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b137c998bdaa2555d912e8fa", +"postgres": "b137c998bdaa2555d912e8fa", +"postgresql": "b137c998bdaa2555d912e8fa", +"snowflake": "b137c998bdaa2555d912e8fa", +"tsql": "b137c998bdaa2555d912e8fa" +} +], +[ +"type", +{ +"DuckDB": "7a924f1a13854aa3260f3f57", +"None": "7a924f1a13854aa3260f3f57", +"bigquery": "7a924f1a13854aa3260f3f57", +"databricks": "7a924f1a13854aa3260f3f57", +"duckdb": "7a924f1a13854aa3260f3f57", +"generic": "7a924f1a13854aa3260f3f57", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7a924f1a13854aa3260f3f57", +"postgres": "7a924f1a13854aa3260f3f57", +"postgresql": "7a924f1a13854aa3260f3f57", +"snowflake": "7a924f1a13854aa3260f3f57", +"tsql": "7a924f1a13854aa3260f3f57" +} +], +[ +"type", +{ +"DuckDB": "66f7ca02161349d637fa7d4e", +"None": "66f7ca02161349d637fa7d4e", +"bigquery": "d90a78129f7050a540984f6f", +"databricks": "d90a78129f7050a540984f6f", +"duckdb": "66f7ca02161349d637fa7d4e", +"generic": "66f7ca02161349d637fa7d4e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d90a78129f7050a540984f6f", +"postgres": "66f7ca02161349d637fa7d4e", +"postgresql": "66f7ca02161349d637fa7d4e", +"snowflake": "66f7ca02161349d637fa7d4e", +"tsql": "66f7ca02161349d637fa7d4e" +} +], +[ +"type", +{ +"DuckDB": "104d3e046c47032c37f947b8", +"None": "104d3e046c47032c37f947b8", +"bigquery": "104d3e046c47032c37f947b8", +"databricks": "104d3e046c47032c37f947b8", +"duckdb": "104d3e046c47032c37f947b8", +"generic": "104d3e046c47032c37f947b8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "104d3e046c47032c37f947b8", +"postgres": "104d3e046c47032c37f947b8", +"postgresql": "104d3e046c47032c37f947b8", +"snowflake": "104d3e046c47032c37f947b8", +"tsql": "104d3e046c47032c37f947b8" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "f3150219c38dba6b9f922447", +"None": "f3150219c38dba6b9f922447", +"bigquery": "f3150219c38dba6b9f922447", +"databricks": "f3150219c38dba6b9f922447", +"duckdb": "f3150219c38dba6b9f922447", +"generic": "f3150219c38dba6b9f922447", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f3150219c38dba6b9f922447", +"postgres": "f3150219c38dba6b9f922447", +"postgresql": "f3150219c38dba6b9f922447", +"snowflake": "f3150219c38dba6b9f922447", +"tsql": "f3150219c38dba6b9f922447" +} +], +[ +"type", +{ +"DuckDB": "f6140be64a327a15b42b923d", +"None": "f6140be64a327a15b42b923d", +"bigquery": "f6140be64a327a15b42b923d", +"databricks": "f6140be64a327a15b42b923d", +"duckdb": "f6140be64a327a15b42b923d", +"generic": "f6140be64a327a15b42b923d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f6140be64a327a15b42b923d", +"postgres": "f6140be64a327a15b42b923d", +"postgresql": "f6140be64a327a15b42b923d", +"snowflake": "f6140be64a327a15b42b923d", +"tsql": "f6140be64a327a15b42b923d" +} +], +[ +"type", +{ +"DuckDB": "e408f2d8251e1675179187e6", +"None": "e408f2d8251e1675179187e6", +"bigquery": "e408f2d8251e1675179187e6", +"databricks": "e408f2d8251e1675179187e6", +"duckdb": "e408f2d8251e1675179187e6", +"generic": "e408f2d8251e1675179187e6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e408f2d8251e1675179187e6", +"postgres": "e408f2d8251e1675179187e6", +"postgresql": "e408f2d8251e1675179187e6", +"snowflake": "e408f2d8251e1675179187e6", +"tsql": "e408f2d8251e1675179187e6" +} +], +[ +"type", +{ +"DuckDB": "3959952046272deb40567543", +"None": "3959952046272deb40567543", +"bigquery": "3959952046272deb40567543", +"databricks": "3959952046272deb40567543", +"duckdb": "3959952046272deb40567543", +"generic": "3959952046272deb40567543", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3959952046272deb40567543", +"postgres": "3959952046272deb40567543", +"postgresql": "3959952046272deb40567543", +"snowflake": "3959952046272deb40567543", +"tsql": "3959952046272deb40567543" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "087a9417cb511ff7bcfd6ccb", +"None": "383f8922f4b6f167d384037d", +"bigquery": "383f8922f4b6f167d384037d", +"databricks": "383f8922f4b6f167d384037d", +"duckdb": "087a9417cb511ff7bcfd6ccb", +"generic": "383f8922f4b6f167d384037d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "383f8922f4b6f167d384037d", +"postgres": "383f8922f4b6f167d384037d", +"postgresql": "383f8922f4b6f167d384037d", +"snowflake": "383f8922f4b6f167d384037d", +"tsql": "383f8922f4b6f167d384037d" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "5d0a19a893dbf0c1e6e9f15c", +"None": "5d0a19a893dbf0c1e6e9f15c", +"bigquery": "5d0a19a893dbf0c1e6e9f15c", +"databricks": "5d0a19a893dbf0c1e6e9f15c", +"duckdb": "5d0a19a893dbf0c1e6e9f15c", +"generic": "5d0a19a893dbf0c1e6e9f15c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5d0a19a893dbf0c1e6e9f15c", +"postgres": "5d0a19a893dbf0c1e6e9f15c", +"postgresql": "5d0a19a893dbf0c1e6e9f15c", +"snowflake": "5d0a19a893dbf0c1e6e9f15c", +"tsql": "5d0a19a893dbf0c1e6e9f15c" +} +], +[ +"type", +{ +"DuckDB": "2460e6e632163bbe7ecaf8a2", +"None": "2460e6e632163bbe7ecaf8a2", +"bigquery": "2460e6e632163bbe7ecaf8a2", +"databricks": "2460e6e632163bbe7ecaf8a2", +"duckdb": "2460e6e632163bbe7ecaf8a2", +"generic": "2460e6e632163bbe7ecaf8a2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2460e6e632163bbe7ecaf8a2", +"postgres": "2460e6e632163bbe7ecaf8a2", +"postgresql": "2460e6e632163bbe7ecaf8a2", +"snowflake": "2460e6e632163bbe7ecaf8a2", +"tsql": "2460e6e632163bbe7ecaf8a2" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "3b0d47536653277b7d98e328", +"None": "3b0d47536653277b7d98e328", +"bigquery": "3b0d47536653277b7d98e328", +"databricks": "3b0d47536653277b7d98e328", +"duckdb": "3b0d47536653277b7d98e328", +"generic": "3b0d47536653277b7d98e328", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3b0d47536653277b7d98e328", +"postgres": "3b0d47536653277b7d98e328", +"postgresql": "3b0d47536653277b7d98e328", +"snowflake": "3b0d47536653277b7d98e328", +"tsql": "3b0d47536653277b7d98e328" +} +], +[ +"type", +{ +"DuckDB": "da08a000bb111759556a61cc", +"None": "df518e3c53cd9a166efacdb4", +"bigquery": "0439c1de159d15c43453a07b", +"databricks": "8352e528bde15549f0d0d870", +"duckdb": "da08a000bb111759556a61cc", +"generic": "df518e3c53cd9a166efacdb4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73477f3a6e7e1fd4288ea98f", +"postgres": "df518e3c53cd9a166efacdb4", +"postgresql": "df518e3c53cd9a166efacdb4", +"snowflake": "58581fd57ff3a9d0f74420dc", +"tsql": "e8893244b745b4ab365bbe20" +} +], +[ +"type", +{ +"DuckDB": "1f9deff638e476a7f168220b", +"None": "1f9deff638e476a7f168220b", +"bigquery": "1f9deff638e476a7f168220b", +"databricks": "eeac51d8e73fdd4f598fd1cd", +"duckdb": "1f9deff638e476a7f168220b", +"generic": "1f9deff638e476a7f168220b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eeac51d8e73fdd4f598fd1cd", +"postgres": "1f9deff638e476a7f168220b", +"postgresql": "1f9deff638e476a7f168220b", +"snowflake": "1f9deff638e476a7f168220b", +"tsql": "1f9deff638e476a7f168220b" +} +], +[ +"type", +{ +"DuckDB": "ebfad3a0b99f87bcac831ded", +"None": "ebfad3a0b99f87bcac831ded", +"bigquery": "ebfad3a0b99f87bcac831ded", +"databricks": "ebfad3a0b99f87bcac831ded", +"duckdb": "ebfad3a0b99f87bcac831ded", +"generic": "ebfad3a0b99f87bcac831ded", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebfad3a0b99f87bcac831ded", +"postgres": "ebfad3a0b99f87bcac831ded", +"postgresql": "ebfad3a0b99f87bcac831ded", +"snowflake": "ebfad3a0b99f87bcac831ded", +"tsql": "ebfad3a0b99f87bcac831ded" +} +], +[ +"type", +{ +"DuckDB": "5620e893a908c6743b863bf6", +"None": "5620e893a908c6743b863bf6", +"bigquery": "4281b54de06f589b3507035a", +"databricks": "4281b54de06f589b3507035a", +"duckdb": "5620e893a908c6743b863bf6", +"generic": "5620e893a908c6743b863bf6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4281b54de06f589b3507035a", +"postgres": "5620e893a908c6743b863bf6", +"postgresql": "5620e893a908c6743b863bf6", +"snowflake": "5620e893a908c6743b863bf6", +"tsql": "5620e893a908c6743b863bf6" +} +], +[ +"type", +{ +"DuckDB": "48b9d23d0bb10c99b1325f6b", +"None": "48b9d23d0bb10c99b1325f6b", +"bigquery": "48b9d23d0bb10c99b1325f6b", +"databricks": "48b9d23d0bb10c99b1325f6b", +"duckdb": "48b9d23d0bb10c99b1325f6b", +"generic": "48b9d23d0bb10c99b1325f6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "48b9d23d0bb10c99b1325f6b", +"postgres": "48b9d23d0bb10c99b1325f6b", +"postgresql": "48b9d23d0bb10c99b1325f6b", +"snowflake": "48b9d23d0bb10c99b1325f6b", +"tsql": "48b9d23d0bb10c99b1325f6b" +} +], +[ +"type", +{ +"DuckDB": "024a48135994929163b8f97b", +"None": "8f0cf40e26c5ef0cbf1da302", +"bigquery": "8f0cf40e26c5ef0cbf1da302", +"databricks": "8f0cf40e26c5ef0cbf1da302", +"duckdb": "024a48135994929163b8f97b", +"generic": "8f0cf40e26c5ef0cbf1da302", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "024a48135994929163b8f97b", +"postgresql": "024a48135994929163b8f97b", +"snowflake": "cc36c0fadc1a5838c69f0b9e", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "4d0f0db1a550ed1e0da1a043", +"None": "4d0f0db1a550ed1e0da1a043", +"bigquery": "4d0f0db1a550ed1e0da1a043", +"databricks": "4d0f0db1a550ed1e0da1a043", +"duckdb": "4d0f0db1a550ed1e0da1a043", +"generic": "4d0f0db1a550ed1e0da1a043", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4d0f0db1a550ed1e0da1a043", +"postgres": "4d0f0db1a550ed1e0da1a043", +"postgresql": "4d0f0db1a550ed1e0da1a043", +"snowflake": "4d0f0db1a550ed1e0da1a043", +"tsql": "4d0f0db1a550ed1e0da1a043" +} +], +[ +"type", +{ +"DuckDB": "1275b5b0c52f69cf592c004b", +"None": "1275b5b0c52f69cf592c004b", +"bigquery": "1275b5b0c52f69cf592c004b", +"databricks": "1275b5b0c52f69cf592c004b", +"duckdb": "1275b5b0c52f69cf592c004b", +"generic": "1275b5b0c52f69cf592c004b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1275b5b0c52f69cf592c004b", +"postgres": "1275b5b0c52f69cf592c004b", +"postgresql": "1275b5b0c52f69cf592c004b", +"snowflake": "1275b5b0c52f69cf592c004b", +"tsql": "1275b5b0c52f69cf592c004b" +} +], +[ +"type", +{ +"DuckDB": "fb5907c56dd752ac5f1dbd95", +"None": "fb5907c56dd752ac5f1dbd95", +"bigquery": "fb5907c56dd752ac5f1dbd95", +"databricks": "fb5907c56dd752ac5f1dbd95", +"duckdb": "fb5907c56dd752ac5f1dbd95", +"generic": "fb5907c56dd752ac5f1dbd95", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fb5907c56dd752ac5f1dbd95", +"postgres": "fb5907c56dd752ac5f1dbd95", +"postgresql": "fb5907c56dd752ac5f1dbd95", +"snowflake": "fb5907c56dd752ac5f1dbd95", +"tsql": "fb5907c56dd752ac5f1dbd95" +} +], +[ +"type", +{ +"DuckDB": "c82815abe30b3d099903f838", +"None": "c82815abe30b3d099903f838", +"bigquery": "c82815abe30b3d099903f838", +"databricks": "c82815abe30b3d099903f838", +"duckdb": "c82815abe30b3d099903f838", +"generic": "c82815abe30b3d099903f838", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c82815abe30b3d099903f838", +"postgres": "c82815abe30b3d099903f838", +"postgresql": "c82815abe30b3d099903f838", +"snowflake": "c82815abe30b3d099903f838", +"tsql": "07ca53fdc57a22a78ad75787" +} +], +[ +"type", +{ +"DuckDB": "182a094bae8a9a33f67845ad", +"None": "182a094bae8a9a33f67845ad", +"bigquery": "182a094bae8a9a33f67845ad", +"databricks": "182a094bae8a9a33f67845ad", +"duckdb": "182a094bae8a9a33f67845ad", +"generic": "182a094bae8a9a33f67845ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "182a094bae8a9a33f67845ad", +"postgres": "182a094bae8a9a33f67845ad", +"postgresql": "182a094bae8a9a33f67845ad", +"snowflake": "182a094bae8a9a33f67845ad", +"tsql": "182a094bae8a9a33f67845ad" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "330fec4d6c49ac503e302b43", +"None": "330fec4d6c49ac503e302b43", +"bigquery": "330fec4d6c49ac503e302b43", +"databricks": "330fec4d6c49ac503e302b43", +"duckdb": "330fec4d6c49ac503e302b43", +"generic": "330fec4d6c49ac503e302b43", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "330fec4d6c49ac503e302b43", +"postgres": "f38ef32a483f1c4f89ae309e", +"postgresql": "f38ef32a483f1c4f89ae309e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "330fec4d6c49ac503e302b43" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "8203bd5901a8ec71ff7c103e", +"None": "8203bd5901a8ec71ff7c103e", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "8203bd5901a8ec71ff7c103e", +"duckdb": "8203bd5901a8ec71ff7c103e", +"generic": "8203bd5901a8ec71ff7c103e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8203bd5901a8ec71ff7c103e", +"postgres": "8203bd5901a8ec71ff7c103e", +"postgresql": "8203bd5901a8ec71ff7c103e", +"snowflake": "8203bd5901a8ec71ff7c103e", +"tsql": "8203bd5901a8ec71ff7c103e" +} +], +[ +"type", +{ +"DuckDB": "9ff8ffdba7614214fe44bd7f", +"None": "9ff8ffdba7614214fe44bd7f", +"bigquery": "9ff8ffdba7614214fe44bd7f", +"databricks": "9ff8ffdba7614214fe44bd7f", +"duckdb": "9ff8ffdba7614214fe44bd7f", +"generic": "9ff8ffdba7614214fe44bd7f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9ff8ffdba7614214fe44bd7f", +"postgres": "9ff8ffdba7614214fe44bd7f", +"postgresql": "9ff8ffdba7614214fe44bd7f", +"snowflake": "9ff8ffdba7614214fe44bd7f", +"tsql": "9ff8ffdba7614214fe44bd7f" +} +], +[ +"type", +{ +"DuckDB": "05ebf79d18b2ae4eaa41f450", +"None": "336fd1b7a1f7d8105c29280c", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "336fd1b7a1f7d8105c29280c", +"duckdb": "05ebf79d18b2ae4eaa41f450", +"generic": "336fd1b7a1f7d8105c29280c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "336fd1b7a1f7d8105c29280c", +"postgres": "336fd1b7a1f7d8105c29280c", +"postgresql": "336fd1b7a1f7d8105c29280c", +"snowflake": "336fd1b7a1f7d8105c29280c", +"tsql": "336fd1b7a1f7d8105c29280c" +} +], +[ +"type", +{ +"DuckDB": "fa45b7e4a9ef3b5a9c5781af", +"None": "fa45b7e4a9ef3b5a9c5781af", +"bigquery": "fa45b7e4a9ef3b5a9c5781af", +"databricks": "fa45b7e4a9ef3b5a9c5781af", +"duckdb": "fa45b7e4a9ef3b5a9c5781af", +"generic": "fa45b7e4a9ef3b5a9c5781af", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fa45b7e4a9ef3b5a9c5781af", +"postgres": "fa45b7e4a9ef3b5a9c5781af", +"postgresql": "fa45b7e4a9ef3b5a9c5781af", +"snowflake": "fa45b7e4a9ef3b5a9c5781af", +"tsql": "fa45b7e4a9ef3b5a9c5781af" +} +], +[ +"type", +{ +"DuckDB": "a22797fa0ef22aadba4ac3ec", +"None": "a22797fa0ef22aadba4ac3ec", +"bigquery": "a22797fa0ef22aadba4ac3ec", +"databricks": "a22797fa0ef22aadba4ac3ec", +"duckdb": "a22797fa0ef22aadba4ac3ec", +"generic": "a22797fa0ef22aadba4ac3ec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a22797fa0ef22aadba4ac3ec", +"postgres": "a22797fa0ef22aadba4ac3ec", +"postgresql": "a22797fa0ef22aadba4ac3ec", +"snowflake": "a22797fa0ef22aadba4ac3ec", +"tsql": "a22797fa0ef22aadba4ac3ec" +} +], +[ +"type", +{ +"DuckDB": "881106a4a0d2de0ff7c56373", +"None": "881106a4a0d2de0ff7c56373", +"bigquery": "881106a4a0d2de0ff7c56373", +"databricks": "881106a4a0d2de0ff7c56373", +"duckdb": "881106a4a0d2de0ff7c56373", +"generic": "881106a4a0d2de0ff7c56373", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "881106a4a0d2de0ff7c56373", +"postgres": "881106a4a0d2de0ff7c56373", +"postgresql": "881106a4a0d2de0ff7c56373", +"snowflake": "881106a4a0d2de0ff7c56373", +"tsql": "881106a4a0d2de0ff7c56373" +} +], +[ +"type", +{ +"DuckDB": "2705f19942a4194a72576911", +"None": "2705f19942a4194a72576911", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "2705f19942a4194a72576911", +"duckdb": "2705f19942a4194a72576911", +"generic": "2705f19942a4194a72576911", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2705f19942a4194a72576911", +"postgres": "2705f19942a4194a72576911", +"postgresql": "2705f19942a4194a72576911", +"snowflake": "2705f19942a4194a72576911", +"tsql": "2705f19942a4194a72576911" +} +], +[ +"type", +{ +"DuckDB": "79483dab720e706b74936744", +"None": "79483dab720e706b74936744", +"bigquery": "79483dab720e706b74936744", +"databricks": "79483dab720e706b74936744", +"duckdb": "79483dab720e706b74936744", +"generic": "79483dab720e706b74936744", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79483dab720e706b74936744", +"postgres": "79483dab720e706b74936744", +"postgresql": "79483dab720e706b74936744", +"snowflake": "79483dab720e706b74936744", +"tsql": "79483dab720e706b74936744" +} +], +[ +"type", +{ +"DuckDB": "7032aa8254308ba815883957", +"None": "7032aa8254308ba815883957", +"bigquery": "7032aa8254308ba815883957", +"databricks": "7032aa8254308ba815883957", +"duckdb": "7032aa8254308ba815883957", +"generic": "7032aa8254308ba815883957", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7032aa8254308ba815883957", +"postgres": "7032aa8254308ba815883957", +"postgresql": "7032aa8254308ba815883957", +"snowflake": "7032aa8254308ba815883957", +"tsql": "7032aa8254308ba815883957" +} +], +[ +"type", +{ +"DuckDB": "6f763a99e9c69109526b40c0", +"None": "6f763a99e9c69109526b40c0", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "6f763a99e9c69109526b40c0", +"duckdb": "6f763a99e9c69109526b40c0", +"generic": "6f763a99e9c69109526b40c0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6f763a99e9c69109526b40c0", +"postgres": "6f763a99e9c69109526b40c0", +"postgresql": "6f763a99e9c69109526b40c0", +"snowflake": "2705f19942a4194a72576911", +"tsql": "6f763a99e9c69109526b40c0" +} +], +[ +"type", +{ +"DuckDB": "d9a22eb846c0561f4d74700b", +"None": "d9a22eb846c0561f4d74700b", +"bigquery": "d9a22eb846c0561f4d74700b", +"databricks": "d9a22eb846c0561f4d74700b", +"duckdb": "d9a22eb846c0561f4d74700b", +"generic": "d9a22eb846c0561f4d74700b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d9a22eb846c0561f4d74700b", +"postgres": "d9a22eb846c0561f4d74700b", +"postgresql": "d9a22eb846c0561f4d74700b", +"snowflake": "9b5f44707b5f4764b82741be", +"tsql": "d9a22eb846c0561f4d74700b" +} +], +[ +"type", +{ +"DuckDB": "ce3765fd18ee6aff1394ac23", +"None": "ce3765fd18ee6aff1394ac23", +"bigquery": "ce3765fd18ee6aff1394ac23", +"databricks": "ce3765fd18ee6aff1394ac23", +"duckdb": "ce3765fd18ee6aff1394ac23", +"generic": "ce3765fd18ee6aff1394ac23", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ce3765fd18ee6aff1394ac23", +"postgres": "ce3765fd18ee6aff1394ac23", +"postgresql": "ce3765fd18ee6aff1394ac23", +"snowflake": "ce3765fd18ee6aff1394ac23", +"tsql": "ce3765fd18ee6aff1394ac23" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "78b2cf275c640af54feeca31", +"None": "78b2cf275c640af54feeca31", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "78b2cf275c640af54feeca31", +"duckdb": "78b2cf275c640af54feeca31", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "78b2cf275c640af54feeca31", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "87eaac3f093587d1d34762da", +"None": "87eaac3f093587d1d34762da", +"bigquery": "87eaac3f093587d1d34762da", +"databricks": "87eaac3f093587d1d34762da", +"duckdb": "87eaac3f093587d1d34762da", +"generic": "87eaac3f093587d1d34762da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "87eaac3f093587d1d34762da", +"postgres": "87eaac3f093587d1d34762da", +"postgresql": "87eaac3f093587d1d34762da", +"snowflake": "87eaac3f093587d1d34762da", +"tsql": "87eaac3f093587d1d34762da" +} +], +[ +"type", +{ +"DuckDB": "23ee196b7bd6039855a1460f", +"None": "23ee196b7bd6039855a1460f", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "23ee196b7bd6039855a1460f", +"duckdb": "23ee196b7bd6039855a1460f", +"generic": "23ee196b7bd6039855a1460f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23ee196b7bd6039855a1460f", +"postgres": "23ee196b7bd6039855a1460f", +"postgresql": "23ee196b7bd6039855a1460f", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "23ee196b7bd6039855a1460f" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "97cf3d84c55104df9751ec06", +"None": "97cf3d84c55104df9751ec06", +"bigquery": "97cf3d84c55104df9751ec06", +"databricks": "97cf3d84c55104df9751ec06", +"duckdb": "97cf3d84c55104df9751ec06", +"generic": "97cf3d84c55104df9751ec06", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "97cf3d84c55104df9751ec06", +"postgres": "97cf3d84c55104df9751ec06", +"postgresql": "97cf3d84c55104df9751ec06", +"snowflake": "97cf3d84c55104df9751ec06", +"tsql": "97cf3d84c55104df9751ec06" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "18db5afe2c1e80d1ee652b54", +"None": "18db5afe2c1e80d1ee652b54", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "18db5afe2c1e80d1ee652b54", +"duckdb": "18db5afe2c1e80d1ee652b54", +"generic": "18db5afe2c1e80d1ee652b54", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "18db5afe2c1e80d1ee652b54", +"postgres": "18db5afe2c1e80d1ee652b54", +"postgresql": "18db5afe2c1e80d1ee652b54", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "18db5afe2c1e80d1ee652b54" +} +], +[ +"type", +{ +"DuckDB": "5e46ad7c70253689a5c6bb7f", +"None": "5e46ad7c70253689a5c6bb7f", +"bigquery": "5e46ad7c70253689a5c6bb7f", +"databricks": "5e46ad7c70253689a5c6bb7f", +"duckdb": "5e46ad7c70253689a5c6bb7f", +"generic": "5e46ad7c70253689a5c6bb7f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5e46ad7c70253689a5c6bb7f", +"postgres": "5e46ad7c70253689a5c6bb7f", +"postgresql": "5e46ad7c70253689a5c6bb7f", +"snowflake": "5e46ad7c70253689a5c6bb7f", +"tsql": "5e46ad7c70253689a5c6bb7f" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "8789309b90fc585ddbbe90f9", +"None": "8789309b90fc585ddbbe90f9", +"bigquery": "8789309b90fc585ddbbe90f9", +"databricks": "8789309b90fc585ddbbe90f9", +"duckdb": "8789309b90fc585ddbbe90f9", +"generic": "8789309b90fc585ddbbe90f9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8789309b90fc585ddbbe90f9", +"postgres": "8789309b90fc585ddbbe90f9", +"postgresql": "8789309b90fc585ddbbe90f9", +"snowflake": "8789309b90fc585ddbbe90f9", +"tsql": "8789309b90fc585ddbbe90f9" +} +], +[ +"type", +{ +"DuckDB": "5990107b647ce24001477492", +"None": "5990107b647ce24001477492", +"bigquery": "5990107b647ce24001477492", +"databricks": "5990107b647ce24001477492", +"duckdb": "5990107b647ce24001477492", +"generic": "5990107b647ce24001477492", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5990107b647ce24001477492", +"postgres": "5990107b647ce24001477492", +"postgresql": "5990107b647ce24001477492", +"snowflake": "5990107b647ce24001477492", +"tsql": "5990107b647ce24001477492" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "f15c56475abfc73488abe63b", +"None": "f15c56475abfc73488abe63b", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f15c56475abfc73488abe63b", +"duckdb": "f15c56475abfc73488abe63b", +"generic": "f15c56475abfc73488abe63b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f15c56475abfc73488abe63b", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f15c56475abfc73488abe63b" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "8317b957016bdf11135b4836", +"None": "0a21e78538ff353d773b4eea", +"bigquery": "0a21e78538ff353d773b4eea", +"databricks": "0a21e78538ff353d773b4eea", +"duckdb": "8317b957016bdf11135b4836", +"generic": "0a21e78538ff353d773b4eea", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8317b957016bdf11135b4836", +"postgresql": "8317b957016bdf11135b4836", +"snowflake": "c2976683dc7003c9f4e8d7d0", +"tsql": "45310b1154cae517b4caedff" +} +], +[ +"type", +{ +"DuckDB": "e72e438b895cc91eaf4be018", +"None": "a1b6a3abd5c2785b419400de", +"bigquery": "f4105e8e41859869d2746e1d", +"databricks": "a1b6a3abd5c2785b419400de", +"duckdb": "e72e438b895cc91eaf4be018", +"generic": "a1b6a3abd5c2785b419400de", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a1b6a3abd5c2785b419400de", +"postgres": "a1b6a3abd5c2785b419400de", +"postgresql": "a1b6a3abd5c2785b419400de", +"snowflake": "6031cfaf0d57e25f8a111524", +"tsql": "a1b6a3abd5c2785b419400de" +} +], +[ +"type", +{ +"DuckDB": "c729c38d2df6efedc6288247", +"None": "eea1002d88cc68fad7424aca", +"bigquery": "eea1002d88cc68fad7424aca", +"databricks": "eea1002d88cc68fad7424aca", +"duckdb": "c729c38d2df6efedc6288247", +"generic": "eea1002d88cc68fad7424aca", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eea1002d88cc68fad7424aca", +"postgres": "eea1002d88cc68fad7424aca", +"postgresql": "eea1002d88cc68fad7424aca", +"snowflake": "eea1002d88cc68fad7424aca", +"tsql": "eea1002d88cc68fad7424aca" +} +], +[ +"type", +{ +"DuckDB": "1ebac487198b6c90b027faab", +"None": "1ebac487198b6c90b027faab", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "1ebac487198b6c90b027faab", +"duckdb": "1ebac487198b6c90b027faab", +"generic": "1ebac487198b6c90b027faab", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1ebac487198b6c90b027faab", +"postgres": "1ebac487198b6c90b027faab", +"postgresql": "1ebac487198b6c90b027faab", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c692d974ee3f362ecb38f27c" +} +], +[ +"type", +{ +"DuckDB": "110327dc45eac0e8d54a96dc", +"None": "110327dc45eac0e8d54a96dc", +"bigquery": "1a804f317a3e9a0016925b04", +"databricks": "1a804f317a3e9a0016925b04", +"duckdb": "110327dc45eac0e8d54a96dc", +"generic": "110327dc45eac0e8d54a96dc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1a804f317a3e9a0016925b04", +"postgres": "110327dc45eac0e8d54a96dc", +"postgresql": "110327dc45eac0e8d54a96dc", +"snowflake": "110327dc45eac0e8d54a96dc", +"tsql": "110327dc45eac0e8d54a96dc" +} +], +[ +"type", +{ +"DuckDB": "8562dfdaf4f03fa06585b288", +"None": "8562dfdaf4f03fa06585b288", +"bigquery": "8562dfdaf4f03fa06585b288", +"databricks": "8562dfdaf4f03fa06585b288", +"duckdb": "8562dfdaf4f03fa06585b288", +"generic": "8562dfdaf4f03fa06585b288", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8562dfdaf4f03fa06585b288", +"postgres": "8562dfdaf4f03fa06585b288", +"postgresql": "8562dfdaf4f03fa06585b288", +"snowflake": "8562dfdaf4f03fa06585b288", +"tsql": "8562dfdaf4f03fa06585b288" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "a0f9ede1ffaaa874ee2066b3", +"None": "a0f9ede1ffaaa874ee2066b3", +"bigquery": "ba7f36f1ed1e8dae4a3a836e", +"databricks": "0111ff4826a58e7709648841", +"duckdb": "a0f9ede1ffaaa874ee2066b3", +"generic": "a0f9ede1ffaaa874ee2066b3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a0f9ede1ffaaa874ee2066b3", +"postgres": "be386e1639549e84c97742c5", +"postgresql": "be386e1639549e84c97742c5", +"snowflake": "8c43a62af28ed0ed164b472b", +"tsql": "a0f9ede1ffaaa874ee2066b3" +} +], +[ +"type", +{ +"DuckDB": "733b04338829c5844270d25e", +"None": "733b04338829c5844270d25e", +"bigquery": "733b04338829c5844270d25e", +"databricks": "733b04338829c5844270d25e", +"duckdb": "733b04338829c5844270d25e", +"generic": "733b04338829c5844270d25e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "733b04338829c5844270d25e", +"postgres": "733b04338829c5844270d25e", +"postgresql": "733b04338829c5844270d25e", +"snowflake": "733b04338829c5844270d25e", +"tsql": "733b04338829c5844270d25e" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "7cd31289a5b99b5a5e24492d", +"None": "7cd31289a5b99b5a5e24492d", +"bigquery": "7cd31289a5b99b5a5e24492d", +"databricks": "7cd31289a5b99b5a5e24492d", +"duckdb": "7cd31289a5b99b5a5e24492d", +"generic": "7cd31289a5b99b5a5e24492d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7cd31289a5b99b5a5e24492d", +"postgres": "7cd31289a5b99b5a5e24492d", +"postgresql": "7cd31289a5b99b5a5e24492d", +"snowflake": "7cd31289a5b99b5a5e24492d", +"tsql": "7cd31289a5b99b5a5e24492d" +} +], +[ +"type", +{ +"DuckDB": "a9d9e03f8e981cec9dca7694", +"None": "a9d9e03f8e981cec9dca7694", +"bigquery": "a9d9e03f8e981cec9dca7694", +"databricks": "a9d9e03f8e981cec9dca7694", +"duckdb": "a9d9e03f8e981cec9dca7694", +"generic": "a9d9e03f8e981cec9dca7694", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a9d9e03f8e981cec9dca7694", +"postgres": "a9d9e03f8e981cec9dca7694", +"postgresql": "a9d9e03f8e981cec9dca7694", +"snowflake": "a9d9e03f8e981cec9dca7694", +"tsql": "a9d9e03f8e981cec9dca7694" +} +], +[ +"type", +{ +"DuckDB": "8b8d5f75ba6dec68b4cebc6b", +"None": "8b8d5f75ba6dec68b4cebc6b", +"bigquery": "8b8d5f75ba6dec68b4cebc6b", +"databricks": "8b8d5f75ba6dec68b4cebc6b", +"duckdb": "8b8d5f75ba6dec68b4cebc6b", +"generic": "8b8d5f75ba6dec68b4cebc6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8b8d5f75ba6dec68b4cebc6b", +"postgres": "8b8d5f75ba6dec68b4cebc6b", +"postgresql": "8b8d5f75ba6dec68b4cebc6b", +"snowflake": "8b8d5f75ba6dec68b4cebc6b", +"tsql": "8b8d5f75ba6dec68b4cebc6b" +} +], +[ +"type", +{ +"DuckDB": "3959952046272deb40567543", +"None": "3959952046272deb40567543", +"bigquery": "3959952046272deb40567543", +"databricks": "3959952046272deb40567543", +"duckdb": "3959952046272deb40567543", +"generic": "3959952046272deb40567543", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3959952046272deb40567543", +"postgres": "3959952046272deb40567543", +"postgresql": "3959952046272deb40567543", +"snowflake": "3959952046272deb40567543", +"tsql": "3959952046272deb40567543" +} +], +[ +"type", +{ +"DuckDB": "c5d56d00f80f43bfec07a7c2", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "c5d56d00f80f43bfec07a7c2", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "251aba3fef503ce64556c701", +"postgresql": "251aba3fef503ce64556c701", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "251aba3fef503ce64556c701" +} +], +[ +"type", +{ +"DuckDB": "5855bf076c2cc936300b1e0a", +"None": "5855bf076c2cc936300b1e0a", +"bigquery": "5855bf076c2cc936300b1e0a", +"databricks": "5855bf076c2cc936300b1e0a", +"duckdb": "5855bf076c2cc936300b1e0a", +"generic": "5855bf076c2cc936300b1e0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5855bf076c2cc936300b1e0a", +"postgres": "5855bf076c2cc936300b1e0a", +"postgresql": "5855bf076c2cc936300b1e0a", +"snowflake": "5855bf076c2cc936300b1e0a", +"tsql": "5855bf076c2cc936300b1e0a" +} +], +[ +"type", +{ +"DuckDB": "d9315b336b73c3d8d4b6e9a3", +"None": "f10bdd4d40eb495cbcecd31d", +"bigquery": "f10bdd4d40eb495cbcecd31d", +"databricks": "f10bdd4d40eb495cbcecd31d", +"duckdb": "d9315b336b73c3d8d4b6e9a3", +"generic": "f10bdd4d40eb495cbcecd31d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f2ed52f6bf46b4db9343551c", +"postgres": "f2ed52f6bf46b4db9343551c", +"postgresql": "f2ed52f6bf46b4db9343551c", +"snowflake": "f2ed52f6bf46b4db9343551c", +"tsql": "f2ed52f6bf46b4db9343551c" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "42f8fe02ce664d09ff39167c", +"None": "42f8fe02ce664d09ff39167c", +"bigquery": "42f8fe02ce664d09ff39167c", +"databricks": "42f8fe02ce664d09ff39167c", +"duckdb": "42f8fe02ce664d09ff39167c", +"generic": "42f8fe02ce664d09ff39167c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42f8fe02ce664d09ff39167c", +"postgres": "42f8fe02ce664d09ff39167c", +"postgresql": "42f8fe02ce664d09ff39167c", +"snowflake": "42f8fe02ce664d09ff39167c", +"tsql": "42f8fe02ce664d09ff39167c" +} +], +[ +"type", +{ +"DuckDB": "4ebb6333caac069be16c795d", +"None": "4ebb6333caac069be16c795d", +"bigquery": "4ebb6333caac069be16c795d", +"databricks": "4ebb6333caac069be16c795d", +"duckdb": "4ebb6333caac069be16c795d", +"generic": "4ebb6333caac069be16c795d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4ebb6333caac069be16c795d", +"postgres": "4ebb6333caac069be16c795d", +"postgresql": "4ebb6333caac069be16c795d", +"snowflake": "4ebb6333caac069be16c795d", +"tsql": "4ebb6333caac069be16c795d" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "c844c96e1e3ffc72d5f90df9", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "c844c96e1e3ffc72d5f90df9", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "c844c96e1e3ffc72d5f90df9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "c844c96e1e3ffc72d5f90df9", +"postgresql": "c844c96e1e3ffc72d5f90df9", +"snowflake": "c844c96e1e3ffc72d5f90df9", +"tsql": "a4a176ec3c56e2704a650963" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "3915f3f73328f0838497b2d5", +"None": "3915f3f73328f0838497b2d5", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "3915f3f73328f0838497b2d5", +"duckdb": "3915f3f73328f0838497b2d5", +"generic": "3915f3f73328f0838497b2d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3915f3f73328f0838497b2d5", +"postgres": "3915f3f73328f0838497b2d5", +"postgresql": "3915f3f73328f0838497b2d5", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3915f3f73328f0838497b2d5" +} +], +[ +"type", +{ +"DuckDB": "52e79b501b430a4c2b097344", +"None": "52e79b501b430a4c2b097344", +"bigquery": "52e79b501b430a4c2b097344", +"databricks": "52e79b501b430a4c2b097344", +"duckdb": "52e79b501b430a4c2b097344", +"generic": "52e79b501b430a4c2b097344", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "52e79b501b430a4c2b097344", +"postgres": "52e79b501b430a4c2b097344", +"postgresql": "52e79b501b430a4c2b097344", +"snowflake": "52e79b501b430a4c2b097344", +"tsql": "52e79b501b430a4c2b097344" +} +], +[ +"type", +{ +"DuckDB": "13f1401e7cd410600a4c9485", +"None": "13f1401e7cd410600a4c9485", +"bigquery": "74a30f6aa9def0b58afc616c", +"databricks": "13f1401e7cd410600a4c9485", +"duckdb": "13f1401e7cd410600a4c9485", +"generic": "13f1401e7cd410600a4c9485", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "13f1401e7cd410600a4c9485", +"postgres": "13f1401e7cd410600a4c9485", +"postgresql": "13f1401e7cd410600a4c9485", +"snowflake": "9b5f44707b5f4764b82741be", +"tsql": "13f1401e7cd410600a4c9485" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "802f1c1fcb97ddd5679172d2", +"None": "802f1c1fcb97ddd5679172d2", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "802f1c1fcb97ddd5679172d2", +"duckdb": "802f1c1fcb97ddd5679172d2", +"generic": "802f1c1fcb97ddd5679172d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "802f1c1fcb97ddd5679172d2", +"postgres": "802f1c1fcb97ddd5679172d2", +"postgresql": "802f1c1fcb97ddd5679172d2", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "08784560ad78e14a02cc066f" +} +], +[ +"type", +{ +"DuckDB": "60a23b7dcae6c79f4de83068", +"None": "60a23b7dcae6c79f4de83068", +"bigquery": "60a23b7dcae6c79f4de83068", +"databricks": "60a23b7dcae6c79f4de83068", +"duckdb": "60a23b7dcae6c79f4de83068", +"generic": "60a23b7dcae6c79f4de83068", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60a23b7dcae6c79f4de83068", +"postgres": "60a23b7dcae6c79f4de83068", +"postgresql": "60a23b7dcae6c79f4de83068", +"snowflake": "60a23b7dcae6c79f4de83068", +"tsql": "60a23b7dcae6c79f4de83068" +} +], +[ +"type", +{ +"DuckDB": "4b356542044f1267fcfc487a", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "4b356542044f1267fcfc487a", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "3315bb12afea7b182498ce28", +"None": "3315bb12afea7b182498ce28", +"bigquery": "3315bb12afea7b182498ce28", +"databricks": "3315bb12afea7b182498ce28", +"duckdb": "3315bb12afea7b182498ce28", +"generic": "3315bb12afea7b182498ce28", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3315bb12afea7b182498ce28", +"postgres": "3315bb12afea7b182498ce28", +"postgresql": "3315bb12afea7b182498ce28", +"snowflake": "3315bb12afea7b182498ce28", +"tsql": "3315bb12afea7b182498ce28" +} +], +[ +"type", +{ +"DuckDB": "2844656bba2e199281ab8d9b", +"None": "2844656bba2e199281ab8d9b", +"bigquery": "916f1a306af797bd328aec64", +"databricks": "916f1a306af797bd328aec64", +"duckdb": "2844656bba2e199281ab8d9b", +"generic": "2844656bba2e199281ab8d9b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "916f1a306af797bd328aec64", +"postgres": "2844656bba2e199281ab8d9b", +"postgresql": "2844656bba2e199281ab8d9b", +"snowflake": "2844656bba2e199281ab8d9b", +"tsql": "2844656bba2e199281ab8d9b" +} +], +[ +"type", +{ +"DuckDB": "8a7aa7fa75696327320e0add", +"None": "8a7aa7fa75696327320e0add", +"bigquery": "e62af34c2d31bc9ab880629e", +"databricks": "e62af34c2d31bc9ab880629e", +"duckdb": "8a7aa7fa75696327320e0add", +"generic": "8a7aa7fa75696327320e0add", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e62af34c2d31bc9ab880629e", +"postgres": "8a7aa7fa75696327320e0add", +"postgresql": "8a7aa7fa75696327320e0add", +"snowflake": "8a7aa7fa75696327320e0add", +"tsql": "8a7aa7fa75696327320e0add" +} +], +[ +"type", +{ +"DuckDB": "cbea30a905a72d3b17dc7211", +"None": "cbea30a905a72d3b17dc7211", +"bigquery": "cbea30a905a72d3b17dc7211", +"databricks": "cbea30a905a72d3b17dc7211", +"duckdb": "cbea30a905a72d3b17dc7211", +"generic": "cbea30a905a72d3b17dc7211", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cbea30a905a72d3b17dc7211", +"postgres": "cbea30a905a72d3b17dc7211", +"postgresql": "cbea30a905a72d3b17dc7211", +"snowflake": "cbea30a905a72d3b17dc7211", +"tsql": "cbea30a905a72d3b17dc7211" +} +], +[ +"type", +{ +"DuckDB": "f15c56475abfc73488abe63b", +"None": "f15c56475abfc73488abe63b", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f15c56475abfc73488abe63b", +"duckdb": "f15c56475abfc73488abe63b", +"generic": "f15c56475abfc73488abe63b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f15c56475abfc73488abe63b", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f15c56475abfc73488abe63b" +} +], +[ +"type", +{ +"DuckDB": "e8b6e0bd2ded7b9eec6209e4", +"None": "e8b6e0bd2ded7b9eec6209e4", +"bigquery": "e8b6e0bd2ded7b9eec6209e4", +"databricks": "e8b6e0bd2ded7b9eec6209e4", +"duckdb": "e8b6e0bd2ded7b9eec6209e4", +"generic": "e8b6e0bd2ded7b9eec6209e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8b6e0bd2ded7b9eec6209e4", +"postgres": "e8b6e0bd2ded7b9eec6209e4", +"postgresql": "e8b6e0bd2ded7b9eec6209e4", +"snowflake": "e8b6e0bd2ded7b9eec6209e4", +"tsql": "e8b6e0bd2ded7b9eec6209e4" +} +], +[ +"type", +{ +"DuckDB": "1d8b945fd793ecdcacb90c21", +"None": "1d8b945fd793ecdcacb90c21", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "1d8b945fd793ecdcacb90c21", +"generic": "1d8b945fd793ecdcacb90c21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "4e10d2f9eb8994ead08d1761", +"None": "4e10d2f9eb8994ead08d1761", +"bigquery": "4e10d2f9eb8994ead08d1761", +"databricks": "4e10d2f9eb8994ead08d1761", +"duckdb": "4e10d2f9eb8994ead08d1761", +"generic": "4e10d2f9eb8994ead08d1761", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4e10d2f9eb8994ead08d1761", +"postgres": "4e10d2f9eb8994ead08d1761", +"postgresql": "4e10d2f9eb8994ead08d1761", +"snowflake": "4e10d2f9eb8994ead08d1761", +"tsql": "4e10d2f9eb8994ead08d1761" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "35e216b6921bf843994f2eea", +"None": "46ec3e19e05a6cb29854ccac", +"bigquery": "46ec3e19e05a6cb29854ccac", +"databricks": "46ec3e19e05a6cb29854ccac", +"duckdb": "35e216b6921bf843994f2eea", +"generic": "46ec3e19e05a6cb29854ccac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "46ec3e19e05a6cb29854ccac", +"postgres": "46ec3e19e05a6cb29854ccac", +"postgresql": "46ec3e19e05a6cb29854ccac", +"snowflake": "46ec3e19e05a6cb29854ccac", +"tsql": "46ec3e19e05a6cb29854ccac" +} +], +[ +"type", +{ +"DuckDB": "7515c0db413239044adef879", +"None": "7515c0db413239044adef879", +"bigquery": "f276e907770196606577c932", +"databricks": "7515c0db413239044adef879", +"duckdb": "7515c0db413239044adef879", +"generic": "7515c0db413239044adef879", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7515c0db413239044adef879", +"postgres": "7515c0db413239044adef879", +"postgresql": "7515c0db413239044adef879", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "7515c0db413239044adef879" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "4662a1758442ce44b830269f", +"None": "4662a1758442ce44b830269f", +"bigquery": "4662a1758442ce44b830269f", +"databricks": "4662a1758442ce44b830269f", +"duckdb": "4662a1758442ce44b830269f", +"generic": "4662a1758442ce44b830269f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4662a1758442ce44b830269f", +"postgres": "4662a1758442ce44b830269f", +"postgresql": "4662a1758442ce44b830269f", +"snowflake": "4662a1758442ce44b830269f", +"tsql": "4662a1758442ce44b830269f" +} +], +[ +"type", +{ +"DuckDB": "a55ce7977ac874a3b204217e", +"None": "86b982a98982cd844f498c36", +"bigquery": "86b982a98982cd844f498c36", +"databricks": "86b982a98982cd844f498c36", +"duckdb": "a55ce7977ac874a3b204217e", +"generic": "86b982a98982cd844f498c36", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "a55ce7977ac874a3b204217e", +"postgresql": "a55ce7977ac874a3b204217e", +"snowflake": "5a4429eb67014c1c8f9e10d4", +"tsql": "a13763a505b15f311cd22d08" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "bbb97828cd28c64282709796", +"None": "bbb97828cd28c64282709796", +"bigquery": "bbb97828cd28c64282709796", +"databricks": "bbb97828cd28c64282709796", +"duckdb": "bbb97828cd28c64282709796", +"generic": "bbb97828cd28c64282709796", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bbb97828cd28c64282709796", +"postgres": "bbb97828cd28c64282709796", +"postgresql": "bbb97828cd28c64282709796", +"snowflake": "bbb97828cd28c64282709796", +"tsql": "bbb97828cd28c64282709796" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "02861a11fe2b8f1a7351cb12", +"None": "3abc5e3c14192017bfd1ead5", +"bigquery": "f2c7751cca55dc87c36b6fc2", +"databricks": "f2c7751cca55dc87c36b6fc2", +"duckdb": "02861a11fe2b8f1a7351cb12", +"generic": "3abc5e3c14192017bfd1ead5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f2c7751cca55dc87c36b6fc2", +"postgres": "35efca2fc5df9bbd4013257a", +"postgresql": "35efca2fc5df9bbd4013257a", +"snowflake": "5b65e3054ea3260a0a6dd837", +"tsql": "f2c7751cca55dc87c36b6fc2" +} +], +[ +"type", +{ +"DuckDB": "d1a9a884331f069d89b0935f", +"None": "d1a9a884331f069d89b0935f", +"bigquery": "efe77000c8cb77e8bcc49328", +"databricks": "efe77000c8cb77e8bcc49328", +"duckdb": "d1a9a884331f069d89b0935f", +"generic": "d1a9a884331f069d89b0935f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "efe77000c8cb77e8bcc49328", +"postgres": "d1a9a884331f069d89b0935f", +"postgresql": "d1a9a884331f069d89b0935f", +"snowflake": "d1a9a884331f069d89b0935f", +"tsql": "d1a9a884331f069d89b0935f" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "bce60580ef3a1dc4ce569fff", +"None": "bce60580ef3a1dc4ce569fff", +"bigquery": "bce60580ef3a1dc4ce569fff", +"databricks": "bce60580ef3a1dc4ce569fff", +"duckdb": "bce60580ef3a1dc4ce569fff", +"generic": "bce60580ef3a1dc4ce569fff", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bce60580ef3a1dc4ce569fff", +"postgres": "bce60580ef3a1dc4ce569fff", +"postgresql": "bce60580ef3a1dc4ce569fff", +"snowflake": "bce60580ef3a1dc4ce569fff", +"tsql": "bce60580ef3a1dc4ce569fff" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "cc33965a669608ec39e8c246", +"None": "cc33965a669608ec39e8c246", +"bigquery": "cc33965a669608ec39e8c246", +"databricks": "cc33965a669608ec39e8c246", +"duckdb": "cc33965a669608ec39e8c246", +"generic": "cc33965a669608ec39e8c246", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cc33965a669608ec39e8c246", +"postgres": "cc33965a669608ec39e8c246", +"postgresql": "cc33965a669608ec39e8c246", +"snowflake": "cc33965a669608ec39e8c246", +"tsql": "cc33965a669608ec39e8c246" +} +], +[ +"type", +{ +"DuckDB": "4381456d770bb42050076d00", +"None": "4381456d770bb42050076d00", +"bigquery": "4381456d770bb42050076d00", +"databricks": "4381456d770bb42050076d00", +"duckdb": "4381456d770bb42050076d00", +"generic": "4381456d770bb42050076d00", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4381456d770bb42050076d00", +"postgres": "4381456d770bb42050076d00", +"postgresql": "4381456d770bb42050076d00", +"snowflake": "4381456d770bb42050076d00", +"tsql": "4381456d770bb42050076d00" +} +], +[ +"type", +{ +"DuckDB": "a34c63be87b77d81239d0b4b", +"None": "a34c63be87b77d81239d0b4b", +"bigquery": "a34c63be87b77d81239d0b4b", +"databricks": "a34c63be87b77d81239d0b4b", +"duckdb": "a34c63be87b77d81239d0b4b", +"generic": "a34c63be87b77d81239d0b4b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a34c63be87b77d81239d0b4b", +"postgres": "a34c63be87b77d81239d0b4b", +"postgresql": "a34c63be87b77d81239d0b4b", +"snowflake": "a34c63be87b77d81239d0b4b", +"tsql": "a34c63be87b77d81239d0b4b" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "0d58e20cd83f047b3b85cd1c", +"None": "0d58e20cd83f047b3b85cd1c", +"bigquery": "0d58e20cd83f047b3b85cd1c", +"databricks": "9f2957c784cb1f6b640c10f7", +"duckdb": "0d58e20cd83f047b3b85cd1c", +"generic": "0d58e20cd83f047b3b85cd1c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9f2957c784cb1f6b640c10f7", +"postgres": "0d58e20cd83f047b3b85cd1c", +"postgresql": "0d58e20cd83f047b3b85cd1c", +"snowflake": "0d58e20cd83f047b3b85cd1c", +"tsql": "0d58e20cd83f047b3b85cd1c" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "63e20906fe3504356e04d05e", +"None": "dba9493579be12efd69318ac", +"bigquery": "dba9493579be12efd69318ac", +"databricks": "dba9493579be12efd69318ac", +"duckdb": "63e20906fe3504356e04d05e", +"generic": "dba9493579be12efd69318ac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "63e20906fe3504356e04d05e", +"postgresql": "63e20906fe3504356e04d05e", +"snowflake": "30696690153e91e7ae197982", +"tsql": "d5df2c68a7c80c4cabfc9444" +} +], +[ +"type", +{ +"DuckDB": "7914189ff18b0e70ddf7d042", +"None": "7914189ff18b0e70ddf7d042", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "7914189ff18b0e70ddf7d042", +"duckdb": "7914189ff18b0e70ddf7d042", +"generic": "7914189ff18b0e70ddf7d042", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7914189ff18b0e70ddf7d042", +"postgres": "7914189ff18b0e70ddf7d042", +"postgresql": "7914189ff18b0e70ddf7d042", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "7914189ff18b0e70ddf7d042" +} +], +[ +"type", +{ +"DuckDB": "919b7ec6a7b49d8527b784cd", +"None": "919b7ec6a7b49d8527b784cd", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "919b7ec6a7b49d8527b784cd", +"generic": "919b7ec6a7b49d8527b784cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "919b7ec6a7b49d8527b784cd" +} +], +[ +"type", +{ +"DuckDB": "42cd2b5892468e185781348b", +"None": "42cd2b5892468e185781348b", +"bigquery": "316ab5126bbd68359eedc4d9", +"databricks": "42cd2b5892468e185781348b", +"duckdb": "42cd2b5892468e185781348b", +"generic": "42cd2b5892468e185781348b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42cd2b5892468e185781348b", +"postgres": "42cd2b5892468e185781348b", +"postgresql": "42cd2b5892468e185781348b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "42cd2b5892468e185781348b" +} +], +[ +"type", +{ +"DuckDB": "35e216b6921bf843994f2eea", +"None": "46ec3e19e05a6cb29854ccac", +"bigquery": "46ec3e19e05a6cb29854ccac", +"databricks": "46ec3e19e05a6cb29854ccac", +"duckdb": "35e216b6921bf843994f2eea", +"generic": "46ec3e19e05a6cb29854ccac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "46ec3e19e05a6cb29854ccac", +"postgres": "46ec3e19e05a6cb29854ccac", +"postgresql": "46ec3e19e05a6cb29854ccac", +"snowflake": "46ec3e19e05a6cb29854ccac", +"tsql": "46ec3e19e05a6cb29854ccac" +} +], +[ +"type", +{ +"DuckDB": "eed0645c1e86fa1a6e35ccaa", +"None": "eed0645c1e86fa1a6e35ccaa", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "eed0645c1e86fa1a6e35ccaa", +"duckdb": "eed0645c1e86fa1a6e35ccaa", +"generic": "eed0645c1e86fa1a6e35ccaa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eed0645c1e86fa1a6e35ccaa", +"postgres": "eed0645c1e86fa1a6e35ccaa", +"postgresql": "eed0645c1e86fa1a6e35ccaa", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "eed0645c1e86fa1a6e35ccaa" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "0f3d33062aebbc5edec7b64f", +"None": "0f3d33062aebbc5edec7b64f", +"bigquery": "0f3d33062aebbc5edec7b64f", +"databricks": "544d1979b70121c2f5770b6a", +"duckdb": "0f3d33062aebbc5edec7b64f", +"generic": "0f3d33062aebbc5edec7b64f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "544d1979b70121c2f5770b6a", +"postgres": "0f3d33062aebbc5edec7b64f", +"postgresql": "0f3d33062aebbc5edec7b64f", +"snowflake": "0f3d33062aebbc5edec7b64f", +"tsql": "0f3d33062aebbc5edec7b64f" +} +], +[ +"type", +{ +"DuckDB": "b2c9b6ebca6c097f3caa0aa8", +"None": "b2c9b6ebca6c097f3caa0aa8", +"bigquery": "b2c9b6ebca6c097f3caa0aa8", +"databricks": "b2c9b6ebca6c097f3caa0aa8", +"duckdb": "b2c9b6ebca6c097f3caa0aa8", +"generic": "b2c9b6ebca6c097f3caa0aa8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b2c9b6ebca6c097f3caa0aa8", +"postgres": "b2c9b6ebca6c097f3caa0aa8", +"postgresql": "b2c9b6ebca6c097f3caa0aa8", +"snowflake": "b2c9b6ebca6c097f3caa0aa8", +"tsql": "b2c9b6ebca6c097f3caa0aa8" +} +], +[ +"type", +{ +"DuckDB": "c21a08c5ec73e14b4d409300", +"None": "c21a08c5ec73e14b4d409300", +"bigquery": "c21a08c5ec73e14b4d409300", +"databricks": "c21a08c5ec73e14b4d409300", +"duckdb": "c21a08c5ec73e14b4d409300", +"generic": "c21a08c5ec73e14b4d409300", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c21a08c5ec73e14b4d409300", +"postgres": "c21a08c5ec73e14b4d409300", +"postgresql": "c21a08c5ec73e14b4d409300", +"snowflake": "c21a08c5ec73e14b4d409300", +"tsql": "c21a08c5ec73e14b4d409300" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "88582da8542d7d1abf567520", +"None": "88582da8542d7d1abf567520", +"bigquery": "88582da8542d7d1abf567520", +"databricks": "88582da8542d7d1abf567520", +"duckdb": "88582da8542d7d1abf567520", +"generic": "88582da8542d7d1abf567520", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "88582da8542d7d1abf567520", +"postgres": "88582da8542d7d1abf567520", +"postgresql": "88582da8542d7d1abf567520", +"snowflake": "88582da8542d7d1abf567520", +"tsql": "88582da8542d7d1abf567520" +} +], +[ +"type", +{ +"DuckDB": "8a10388471810a2bd58f7686", +"None": "8a10388471810a2bd58f7686", +"bigquery": "8a10388471810a2bd58f7686", +"databricks": "8a10388471810a2bd58f7686", +"duckdb": "8a10388471810a2bd58f7686", +"generic": "8a10388471810a2bd58f7686", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8a10388471810a2bd58f7686", +"postgres": "8a10388471810a2bd58f7686", +"postgresql": "8a10388471810a2bd58f7686", +"snowflake": "8a10388471810a2bd58f7686", +"tsql": "8a10388471810a2bd58f7686" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "64fc778308b798a730b1d5c9", +"None": "64fc778308b798a730b1d5c9", +"bigquery": "64fc778308b798a730b1d5c9", +"databricks": "64fc778308b798a730b1d5c9", +"duckdb": "64fc778308b798a730b1d5c9", +"generic": "64fc778308b798a730b1d5c9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "64fc778308b798a730b1d5c9", +"postgres": "64fc778308b798a730b1d5c9", +"postgresql": "64fc778308b798a730b1d5c9", +"snowflake": "64fc778308b798a730b1d5c9", +"tsql": "64fc778308b798a730b1d5c9" +} +], +[ +"type", +{ +"DuckDB": "679a90349a55ba7765c07664", +"None": "679a90349a55ba7765c07664", +"bigquery": "679a90349a55ba7765c07664", +"databricks": "679a90349a55ba7765c07664", +"duckdb": "679a90349a55ba7765c07664", +"generic": "679a90349a55ba7765c07664", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "679a90349a55ba7765c07664", +"postgres": "679a90349a55ba7765c07664", +"postgresql": "679a90349a55ba7765c07664", +"snowflake": "679a90349a55ba7765c07664", +"tsql": "679a90349a55ba7765c07664" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "3147a9e06daa6de4a4df5abc", +"None": "3147a9e06daa6de4a4df5abc", +"bigquery": "f276e907770196606577c932", +"databricks": "3147a9e06daa6de4a4df5abc", +"duckdb": "3147a9e06daa6de4a4df5abc", +"generic": "3147a9e06daa6de4a4df5abc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3147a9e06daa6de4a4df5abc", +"postgres": "3147a9e06daa6de4a4df5abc", +"postgresql": "3147a9e06daa6de4a4df5abc", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "3147a9e06daa6de4a4df5abc" +} +], +[ +"type", +{ +"DuckDB": "ff99ba717583eafc6fdd9442", +"None": "a915140f475538632ca59017", +"bigquery": "a915140f475538632ca59017", +"databricks": "a915140f475538632ca59017", +"duckdb": "ff99ba717583eafc6fdd9442", +"generic": "a915140f475538632ca59017", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "39ac50273811909ae35979b7", +"postgresql": "39ac50273811909ae35979b7", +"snowflake": "ca443b97e784bced05b62f0b", +"tsql": "3b16d8eb223ae1333ecbc2bf" +} +], +[ +"type", +{ +"DuckDB": "e7683084f9cefaf3748cf526", +"None": "e7683084f9cefaf3748cf526", +"bigquery": "e7683084f9cefaf3748cf526", +"databricks": "e7683084f9cefaf3748cf526", +"duckdb": "e7683084f9cefaf3748cf526", +"generic": "e7683084f9cefaf3748cf526", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7683084f9cefaf3748cf526", +"postgres": "e7683084f9cefaf3748cf526", +"postgresql": "e7683084f9cefaf3748cf526", +"snowflake": "e7683084f9cefaf3748cf526", +"tsql": "e7683084f9cefaf3748cf526" +} +], +[ +"type", +{ +"DuckDB": "9626d393c137a37008bb4b89", +"None": "9626d393c137a37008bb4b89", +"bigquery": "9626d393c137a37008bb4b89", +"databricks": "9626d393c137a37008bb4b89", +"duckdb": "9626d393c137a37008bb4b89", +"generic": "9626d393c137a37008bb4b89", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9626d393c137a37008bb4b89", +"postgres": "9626d393c137a37008bb4b89", +"postgresql": "9626d393c137a37008bb4b89", +"snowflake": "9626d393c137a37008bb4b89", +"tsql": "9626d393c137a37008bb4b89" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "4e47cae1f2f84f5e523dc43f", +"None": "4e47cae1f2f84f5e523dc43f", +"bigquery": "4e47cae1f2f84f5e523dc43f", +"databricks": "4e47cae1f2f84f5e523dc43f", +"duckdb": "4e47cae1f2f84f5e523dc43f", +"generic": "4e47cae1f2f84f5e523dc43f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4e47cae1f2f84f5e523dc43f", +"postgres": "4e47cae1f2f84f5e523dc43f", +"postgresql": "4e47cae1f2f84f5e523dc43f", +"snowflake": "4e47cae1f2f84f5e523dc43f", +"tsql": "4e47cae1f2f84f5e523dc43f" +} +], +[ +"type", +{ +"DuckDB": "8fe65454f387031aa6231faf", +"None": "8fe65454f387031aa6231faf", +"bigquery": "8fe65454f387031aa6231faf", +"databricks": "8fe65454f387031aa6231faf", +"duckdb": "8fe65454f387031aa6231faf", +"generic": "8fe65454f387031aa6231faf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8fe65454f387031aa6231faf", +"postgres": "8fe65454f387031aa6231faf", +"postgresql": "8fe65454f387031aa6231faf", +"snowflake": "8fe65454f387031aa6231faf", +"tsql": "8fe65454f387031aa6231faf" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "c844c96e1e3ffc72d5f90df9", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "c844c96e1e3ffc72d5f90df9", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "c844c96e1e3ffc72d5f90df9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "c844c96e1e3ffc72d5f90df9", +"postgresql": "c844c96e1e3ffc72d5f90df9", +"snowflake": "c844c96e1e3ffc72d5f90df9", +"tsql": "a4a176ec3c56e2704a650963" +} +], +[ +"type", +{ +"DuckDB": "e8b6e0bd2ded7b9eec6209e4", +"None": "e8b6e0bd2ded7b9eec6209e4", +"bigquery": "e8b6e0bd2ded7b9eec6209e4", +"databricks": "e8b6e0bd2ded7b9eec6209e4", +"duckdb": "e8b6e0bd2ded7b9eec6209e4", +"generic": "e8b6e0bd2ded7b9eec6209e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8b6e0bd2ded7b9eec6209e4", +"postgres": "e8b6e0bd2ded7b9eec6209e4", +"postgresql": "e8b6e0bd2ded7b9eec6209e4", +"snowflake": "e8b6e0bd2ded7b9eec6209e4", +"tsql": "e8b6e0bd2ded7b9eec6209e4" +} +], +[ +"type", +{ +"DuckDB": "d41d40c68507f4d02e348833", +"None": "d41d40c68507f4d02e348833", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "d41d40c68507f4d02e348833", +"duckdb": "d41d40c68507f4d02e348833", +"generic": "d41d40c68507f4d02e348833", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d41d40c68507f4d02e348833", +"postgres": "d41d40c68507f4d02e348833", +"postgresql": "d41d40c68507f4d02e348833", +"snowflake": "d41d40c68507f4d02e348833", +"tsql": "d41d40c68507f4d02e348833" +} +], +[ +"type", +{ +"DuckDB": "0ca1be8f7d61ca627e620450", +"None": "0ca1be8f7d61ca627e620450", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "0ca1be8f7d61ca627e620450", +"duckdb": "0ca1be8f7d61ca627e620450", +"generic": "0ca1be8f7d61ca627e620450", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0ca1be8f7d61ca627e620450", +"postgres": "0ca1be8f7d61ca627e620450", +"postgresql": "0ca1be8f7d61ca627e620450", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "0ca1be8f7d61ca627e620450" +} +], +[ +"type", +{ +"DuckDB": "fbb8dada51c532716b2cbf9f", +"None": "29685ad61061d232d5b44f98", +"bigquery": "29685ad61061d232d5b44f98", +"databricks": "29685ad61061d232d5b44f98", +"duckdb": "fbb8dada51c532716b2cbf9f", +"generic": "29685ad61061d232d5b44f98", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "fbb8dada51c532716b2cbf9f", +"postgresql": "fbb8dada51c532716b2cbf9f", +"snowflake": "3d54d99fa15cb697878997d5", +"tsql": "6846d3f6d21916a4d55dc138" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "64fc778308b798a730b1d5c9", +"None": "64fc778308b798a730b1d5c9", +"bigquery": "64fc778308b798a730b1d5c9", +"databricks": "64fc778308b798a730b1d5c9", +"duckdb": "64fc778308b798a730b1d5c9", +"generic": "64fc778308b798a730b1d5c9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "64fc778308b798a730b1d5c9", +"postgres": "64fc778308b798a730b1d5c9", +"postgresql": "64fc778308b798a730b1d5c9", +"snowflake": "64fc778308b798a730b1d5c9", +"tsql": "64fc778308b798a730b1d5c9" +} +], +[ +"type", +{ +"DuckDB": "ee8c9d9a91ebec8ca7defc90", +"None": "fd3b9424e28fed0134486bc7", +"bigquery": "fd3b9424e28fed0134486bc7", +"databricks": "fd3b9424e28fed0134486bc7", +"duckdb": "ee8c9d9a91ebec8ca7defc90", +"generic": "fd3b9424e28fed0134486bc7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "ee8c9d9a91ebec8ca7defc90", +"postgresql": "ee8c9d9a91ebec8ca7defc90", +"snowflake": "1d67c9120962e3304f0e1bcc", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "01e3dfc024c37a8d62c7f810", +"None": "3a4d8bca5124769db23a43df", +"bigquery": "3a4d8bca5124769db23a43df", +"databricks": "3a4d8bca5124769db23a43df", +"duckdb": "01e3dfc024c37a8d62c7f810", +"generic": "3a4d8bca5124769db23a43df", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "01e3dfc024c37a8d62c7f810", +"postgresql": "01e3dfc024c37a8d62c7f810", +"snowflake": "db03b5ccf153c75e43846a6a", +"tsql": "754720a18c6591b99840f0b6" +} +], +[ +"type", +{ +"DuckDB": "791a58207da7e61895ce33f6", +"None": "791a58207da7e61895ce33f6", +"bigquery": "791a58207da7e61895ce33f6", +"databricks": "791a58207da7e61895ce33f6", +"duckdb": "791a58207da7e61895ce33f6", +"generic": "791a58207da7e61895ce33f6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "791a58207da7e61895ce33f6", +"postgres": "791a58207da7e61895ce33f6", +"postgresql": "791a58207da7e61895ce33f6", +"snowflake": "791a58207da7e61895ce33f6", +"tsql": "791a58207da7e61895ce33f6" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "ef34121d80f6c399a1a456b1", +"None": "ef34121d80f6c399a1a456b1", +"bigquery": "ef34121d80f6c399a1a456b1", +"databricks": "ef34121d80f6c399a1a456b1", +"duckdb": "ef34121d80f6c399a1a456b1", +"generic": "ef34121d80f6c399a1a456b1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ef34121d80f6c399a1a456b1", +"postgres": "ef34121d80f6c399a1a456b1", +"postgresql": "ef34121d80f6c399a1a456b1", +"snowflake": "ef34121d80f6c399a1a456b1", +"tsql": "ef34121d80f6c399a1a456b1" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "b4284f955ab1914d3f556d82", +"None": "b4284f955ab1914d3f556d82", +"bigquery": "b4284f955ab1914d3f556d82", +"databricks": "b4284f955ab1914d3f556d82", +"duckdb": "b4284f955ab1914d3f556d82", +"generic": "b4284f955ab1914d3f556d82", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b4284f955ab1914d3f556d82", +"postgres": "b4284f955ab1914d3f556d82", +"postgresql": "b4284f955ab1914d3f556d82", +"snowflake": "b4284f955ab1914d3f556d82", +"tsql": "b4284f955ab1914d3f556d82" +} +], +[ +"type", +{ +"DuckDB": "c84c0b1d4d7caa6744130456", +"None": "c84c0b1d4d7caa6744130456", +"bigquery": "c84c0b1d4d7caa6744130456", +"databricks": "c84c0b1d4d7caa6744130456", +"duckdb": "c84c0b1d4d7caa6744130456", +"generic": "c84c0b1d4d7caa6744130456", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c84c0b1d4d7caa6744130456", +"postgres": "c84c0b1d4d7caa6744130456", +"postgresql": "c84c0b1d4d7caa6744130456", +"snowflake": "c84c0b1d4d7caa6744130456", +"tsql": "c84c0b1d4d7caa6744130456" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "73151c1398b30e5c5d18f4ce", +"None": "73151c1398b30e5c5d18f4ce", +"bigquery": "73151c1398b30e5c5d18f4ce", +"databricks": "73151c1398b30e5c5d18f4ce", +"duckdb": "73151c1398b30e5c5d18f4ce", +"generic": "73151c1398b30e5c5d18f4ce", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73151c1398b30e5c5d18f4ce", +"postgres": "73151c1398b30e5c5d18f4ce", +"postgresql": "73151c1398b30e5c5d18f4ce", +"snowflake": "73151c1398b30e5c5d18f4ce", +"tsql": "73151c1398b30e5c5d18f4ce" +} +], +[ +"type", +{ +"DuckDB": "792f7d3389f076f50c713946", +"None": "792f7d3389f076f50c713946", +"bigquery": "792f7d3389f076f50c713946", +"databricks": "4fb572024031cb7631777be1", +"duckdb": "792f7d3389f076f50c713946", +"generic": "792f7d3389f076f50c713946", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "792f7d3389f076f50c713946", +"postgres": "792f7d3389f076f50c713946", +"postgresql": "792f7d3389f076f50c713946", +"snowflake": "792f7d3389f076f50c713946", +"tsql": "792f7d3389f076f50c713946" +} +], +[ +"type", +{ +"DuckDB": "236311169ac0526becd627ac", +"None": "236311169ac0526becd627ac", +"bigquery": "236311169ac0526becd627ac", +"databricks": "236311169ac0526becd627ac", +"duckdb": "236311169ac0526becd627ac", +"generic": "236311169ac0526becd627ac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "236311169ac0526becd627ac", +"postgres": "236311169ac0526becd627ac", +"postgresql": "236311169ac0526becd627ac", +"snowflake": "236311169ac0526becd627ac", +"tsql": "236311169ac0526becd627ac" +} +], +[ +"type", +{ +"DuckDB": "05ebf79d18b2ae4eaa41f450", +"None": "336fd1b7a1f7d8105c29280c", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "336fd1b7a1f7d8105c29280c", +"duckdb": "05ebf79d18b2ae4eaa41f450", +"generic": "336fd1b7a1f7d8105c29280c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "336fd1b7a1f7d8105c29280c", +"postgres": "336fd1b7a1f7d8105c29280c", +"postgresql": "336fd1b7a1f7d8105c29280c", +"snowflake": "336fd1b7a1f7d8105c29280c", +"tsql": "336fd1b7a1f7d8105c29280c" +} +], +[ +"type", +{ +"DuckDB": "806dd59533345612c07a861b", +"None": "806dd59533345612c07a861b", +"bigquery": "806dd59533345612c07a861b", +"databricks": "806dd59533345612c07a861b", +"duckdb": "806dd59533345612c07a861b", +"generic": "806dd59533345612c07a861b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "806dd59533345612c07a861b", +"postgres": "806dd59533345612c07a861b", +"postgresql": "806dd59533345612c07a861b", +"snowflake": "806dd59533345612c07a861b", +"tsql": "806dd59533345612c07a861b" +} +], +[ +"type", +{ +"DuckDB": "647ac9829e390656c0783aba", +"None": "1404468a845477f54df838fc", +"bigquery": "2df0f0517ee87dda5683d2bd", +"databricks": "2df0f0517ee87dda5683d2bd", +"duckdb": "647ac9829e390656c0783aba", +"generic": "1404468a845477f54df838fc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2df0f0517ee87dda5683d2bd", +"postgres": "ee5dc4b764837cf002345fb9", +"postgresql": "ee5dc4b764837cf002345fb9", +"snowflake": "8d1f13df6b799b7b301d2e69", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "5aa51d84ac5e6c7a9306f348", +"bigquery": "5aa51d84ac5e6c7a9306f348", +"databricks": "5aa51d84ac5e6c7a9306f348", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "5aa51d84ac5e6c7a9306f348", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "6247229b9f890f25e7b3f0c5", +"None": "eb8ea3778b2debde09f2afb1", +"bigquery": "135a912ea74f76a35d9d0632", +"databricks": "eb8ea3778b2debde09f2afb1", +"duckdb": "6247229b9f890f25e7b3f0c5", +"generic": "eb8ea3778b2debde09f2afb1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "6247229b9f890f25e7b3f0c5", +"postgresql": "6247229b9f890f25e7b3f0c5", +"snowflake": "8f60ea843371b982d069e65b", +"tsql": "3fd7037beaaa396764e49e8b" +} +], +[ +"type", +{ +"DuckDB": "077cc38442b3b9eae7bb07d0", +"None": "a57535ee64678e69d1acd48a", +"bigquery": "a57535ee64678e69d1acd48a", +"databricks": "a57535ee64678e69d1acd48a", +"duckdb": "077cc38442b3b9eae7bb07d0", +"generic": "a57535ee64678e69d1acd48a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "077cc38442b3b9eae7bb07d0", +"postgresql": "077cc38442b3b9eae7bb07d0", +"snowflake": "befa13a14d1144835e866a4d", +"tsql": "2102201dfd2a182bcf503f40" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "ff8f9d6dbbdd5b3cf2e7c198", +"None": "ff8f9d6dbbdd5b3cf2e7c198", +"bigquery": "ff8f9d6dbbdd5b3cf2e7c198", +"databricks": "ff8f9d6dbbdd5b3cf2e7c198", +"duckdb": "ff8f9d6dbbdd5b3cf2e7c198", +"generic": "ff8f9d6dbbdd5b3cf2e7c198", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ff8f9d6dbbdd5b3cf2e7c198", +"postgres": "ff8f9d6dbbdd5b3cf2e7c198", +"postgresql": "ff8f9d6dbbdd5b3cf2e7c198", +"snowflake": "ff8f9d6dbbdd5b3cf2e7c198", +"tsql": "ff8f9d6dbbdd5b3cf2e7c198" +} +], +[ +"type", +{ +"DuckDB": "4928771fd9e503a30457d160", +"None": "4928771fd9e503a30457d160", +"bigquery": "4928771fd9e503a30457d160", +"databricks": "4928771fd9e503a30457d160", +"duckdb": "4928771fd9e503a30457d160", +"generic": "4928771fd9e503a30457d160", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4928771fd9e503a30457d160", +"postgres": "4928771fd9e503a30457d160", +"postgresql": "4928771fd9e503a30457d160", +"snowflake": "4928771fd9e503a30457d160", +"tsql": "4928771fd9e503a30457d160" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "a0e5b4ce93e9317c3cea1cbf", +"None": "a0e5b4ce93e9317c3cea1cbf", +"bigquery": "a0e5b4ce93e9317c3cea1cbf", +"databricks": "c9d23fd01aedfa8b27cf08a6", +"duckdb": "a0e5b4ce93e9317c3cea1cbf", +"generic": "a0e5b4ce93e9317c3cea1cbf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c8a1fc1b91adcea5a885afe3", +"postgres": "a0e5b4ce93e9317c3cea1cbf", +"postgresql": "a0e5b4ce93e9317c3cea1cbf", +"snowflake": "a0e5b4ce93e9317c3cea1cbf", +"tsql": "a0e5b4ce93e9317c3cea1cbf" +} +], +[ +"type", +{ +"DuckDB": "78de20f17ea5d66780a9f32e", +"None": "78de20f17ea5d66780a9f32e", +"bigquery": "78de20f17ea5d66780a9f32e", +"databricks": "78de20f17ea5d66780a9f32e", +"duckdb": "78de20f17ea5d66780a9f32e", +"generic": "78de20f17ea5d66780a9f32e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "78de20f17ea5d66780a9f32e", +"postgres": "78de20f17ea5d66780a9f32e", +"postgresql": "78de20f17ea5d66780a9f32e", +"snowflake": "78de20f17ea5d66780a9f32e", +"tsql": "78de20f17ea5d66780a9f32e" +} +], +[ +"type", +{ +"DuckDB": "8bb8eb3ad4ee794b787a664d", +"None": "8bb8eb3ad4ee794b787a664d", +"bigquery": "01d1b160c1768fa1cd4b2e45", +"databricks": "40f264d27364684659f815eb", +"duckdb": "8bb8eb3ad4ee794b787a664d", +"generic": "8bb8eb3ad4ee794b787a664d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8bb8eb3ad4ee794b787a664d", +"postgres": "8bb8eb3ad4ee794b787a664d", +"postgresql": "8bb8eb3ad4ee794b787a664d", +"snowflake": "469cddcff389dac525f4efdf", +"tsql": "5efacb746122082701b02343" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "3673b4fbbd8a7fed7120ec2b", +"None": "3673b4fbbd8a7fed7120ec2b", +"bigquery": "3673b4fbbd8a7fed7120ec2b", +"databricks": "3673b4fbbd8a7fed7120ec2b", +"duckdb": "3673b4fbbd8a7fed7120ec2b", +"generic": "3673b4fbbd8a7fed7120ec2b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3673b4fbbd8a7fed7120ec2b", +"postgres": "3673b4fbbd8a7fed7120ec2b", +"postgresql": "3673b4fbbd8a7fed7120ec2b", +"snowflake": "3673b4fbbd8a7fed7120ec2b", +"tsql": "3673b4fbbd8a7fed7120ec2b" +} +], +[ +"type", +{ +"DuckDB": "a3b6b25265be330ec52a3322", +"None": "a3b6b25265be330ec52a3322", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "a3b6b25265be330ec52a3322", +"generic": "a3b6b25265be330ec52a3322", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "a3b6b25265be330ec52a3322" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "0b8ad171901cc2b7725617da", +"None": "0b8ad171901cc2b7725617da", +"bigquery": "0b8ad171901cc2b7725617da", +"databricks": "0b8ad171901cc2b7725617da", +"duckdb": "0b8ad171901cc2b7725617da", +"generic": "0b8ad171901cc2b7725617da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b8ad171901cc2b7725617da", +"postgres": "0b8ad171901cc2b7725617da", +"postgresql": "0b8ad171901cc2b7725617da", +"snowflake": "0b8ad171901cc2b7725617da", +"tsql": "0b8ad171901cc2b7725617da" +} +], +[ +"type", +{ +"DuckDB": "208eafc9a5aab337867bb773", +"None": "97dfbcc6528a197a87add69e", +"bigquery": "cd7b6238af592579916341e8", +"databricks": "97dfbcc6528a197a87add69e", +"duckdb": "208eafc9a5aab337867bb773", +"generic": "97dfbcc6528a197a87add69e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "ca3d3fa7d8eb1608bd8752de", +"postgresql": "ca3d3fa7d8eb1608bd8752de", +"snowflake": "46250b305f6ac5a95e4d3083", +"tsql": "4a596251860d8d497d59f1a7" +} +], +[ +"type", +{ +"DuckDB": "c4d601bdc1b1f357b3e9bbdf", +"None": "c4d601bdc1b1f357b3e9bbdf", +"bigquery": "c4d601bdc1b1f357b3e9bbdf", +"databricks": "c4d601bdc1b1f357b3e9bbdf", +"duckdb": "c4d601bdc1b1f357b3e9bbdf", +"generic": "c4d601bdc1b1f357b3e9bbdf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4d601bdc1b1f357b3e9bbdf", +"postgres": "c4d601bdc1b1f357b3e9bbdf", +"postgresql": "c4d601bdc1b1f357b3e9bbdf", +"snowflake": "c4d601bdc1b1f357b3e9bbdf", +"tsql": "c4d601bdc1b1f357b3e9bbdf" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "e7c121d9cbaf6e6b91cfdf74", +"None": "e7c121d9cbaf6e6b91cfdf74", +"bigquery": "e7c121d9cbaf6e6b91cfdf74", +"databricks": "e7c121d9cbaf6e6b91cfdf74", +"duckdb": "e7c121d9cbaf6e6b91cfdf74", +"generic": "e7c121d9cbaf6e6b91cfdf74", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7c121d9cbaf6e6b91cfdf74", +"postgres": "e7c121d9cbaf6e6b91cfdf74", +"postgresql": "e7c121d9cbaf6e6b91cfdf74", +"snowflake": "e7c121d9cbaf6e6b91cfdf74", +"tsql": "e7c121d9cbaf6e6b91cfdf74" +} +], +[ +"type", +{ +"DuckDB": "962ea05bab624c1f8c259a65", +"None": "962ea05bab624c1f8c259a65", +"bigquery": "962ea05bab624c1f8c259a65", +"databricks": "962ea05bab624c1f8c259a65", +"duckdb": "962ea05bab624c1f8c259a65", +"generic": "962ea05bab624c1f8c259a65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "962ea05bab624c1f8c259a65", +"postgres": "962ea05bab624c1f8c259a65", +"postgresql": "962ea05bab624c1f8c259a65", +"snowflake": "962ea05bab624c1f8c259a65", +"tsql": "962ea05bab624c1f8c259a65" +} +], +[ +"type", +{ +"DuckDB": "3e888d249494102b9ba328b2", +"None": "3e888d249494102b9ba328b2", +"bigquery": "3e888d249494102b9ba328b2", +"databricks": "3e888d249494102b9ba328b2", +"duckdb": "3e888d249494102b9ba328b2", +"generic": "3e888d249494102b9ba328b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3e888d249494102b9ba328b2", +"postgres": "3e888d249494102b9ba328b2", +"postgresql": "3e888d249494102b9ba328b2", +"snowflake": "3e888d249494102b9ba328b2", +"tsql": "3e888d249494102b9ba328b2" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "652ff8b21f638eac8fdc46c5", +"None": "652ff8b21f638eac8fdc46c5", +"bigquery": "316ab5126bbd68359eedc4d9", +"databricks": "652ff8b21f638eac8fdc46c5", +"duckdb": "652ff8b21f638eac8fdc46c5", +"generic": "652ff8b21f638eac8fdc46c5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "652ff8b21f638eac8fdc46c5", +"postgres": "652ff8b21f638eac8fdc46c5", +"postgresql": "652ff8b21f638eac8fdc46c5", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "652ff8b21f638eac8fdc46c5" +} +], +[ +"type", +{ +"DuckDB": "0989b67047ee9c0a1d9f2cf4", +"None": "0989b67047ee9c0a1d9f2cf4", +"bigquery": "0989b67047ee9c0a1d9f2cf4", +"databricks": "0989b67047ee9c0a1d9f2cf4", +"duckdb": "0989b67047ee9c0a1d9f2cf4", +"generic": "0989b67047ee9c0a1d9f2cf4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0989b67047ee9c0a1d9f2cf4", +"postgres": "0989b67047ee9c0a1d9f2cf4", +"postgresql": "0989b67047ee9c0a1d9f2cf4", +"snowflake": "0989b67047ee9c0a1d9f2cf4", +"tsql": "0989b67047ee9c0a1d9f2cf4" +} +], +[ +"type", +{ +"DuckDB": "1275b5b0c52f69cf592c004b", +"None": "1275b5b0c52f69cf592c004b", +"bigquery": "1275b5b0c52f69cf592c004b", +"databricks": "1275b5b0c52f69cf592c004b", +"duckdb": "1275b5b0c52f69cf592c004b", +"generic": "1275b5b0c52f69cf592c004b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1275b5b0c52f69cf592c004b", +"postgres": "1275b5b0c52f69cf592c004b", +"postgresql": "1275b5b0c52f69cf592c004b", +"snowflake": "1275b5b0c52f69cf592c004b", +"tsql": "1275b5b0c52f69cf592c004b" +} +], +[ +"type", +{ +"DuckDB": "35d19e3415493d51f36f89b1", +"None": "35d19e3415493d51f36f89b1", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "35d19e3415493d51f36f89b1", +"duckdb": "35d19e3415493d51f36f89b1", +"generic": "35d19e3415493d51f36f89b1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "35d19e3415493d51f36f89b1", +"postgres": "35d19e3415493d51f36f89b1", +"postgresql": "35d19e3415493d51f36f89b1", +"snowflake": "3e611643f0beab4953c4e09b", +"tsql": "35d19e3415493d51f36f89b1" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "b6b82efd03def9465e4f97b2", +"None": "b6b82efd03def9465e4f97b2", +"bigquery": "b6b82efd03def9465e4f97b2", +"databricks": "b6b82efd03def9465e4f97b2", +"duckdb": "b6b82efd03def9465e4f97b2", +"generic": "b6b82efd03def9465e4f97b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b6b82efd03def9465e4f97b2", +"postgres": "b6b82efd03def9465e4f97b2", +"postgresql": "b6b82efd03def9465e4f97b2", +"snowflake": "b6b82efd03def9465e4f97b2", +"tsql": "b6b82efd03def9465e4f97b2" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "80c23e10e486925cf3b33f9d", +"None": "80c23e10e486925cf3b33f9d", +"bigquery": "80c23e10e486925cf3b33f9d", +"databricks": "80c23e10e486925cf3b33f9d", +"duckdb": "80c23e10e486925cf3b33f9d", +"generic": "80c23e10e486925cf3b33f9d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "80c23e10e486925cf3b33f9d", +"postgres": "80c23e10e486925cf3b33f9d", +"postgresql": "80c23e10e486925cf3b33f9d", +"snowflake": "80c23e10e486925cf3b33f9d", +"tsql": "80c23e10e486925cf3b33f9d" +} +], +[ +"type", +{ +"DuckDB": "60a23b7dcae6c79f4de83068", +"None": "60a23b7dcae6c79f4de83068", +"bigquery": "60a23b7dcae6c79f4de83068", +"databricks": "60a23b7dcae6c79f4de83068", +"duckdb": "60a23b7dcae6c79f4de83068", +"generic": "60a23b7dcae6c79f4de83068", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60a23b7dcae6c79f4de83068", +"postgres": "60a23b7dcae6c79f4de83068", +"postgresql": "60a23b7dcae6c79f4de83068", +"snowflake": "60a23b7dcae6c79f4de83068", +"tsql": "60a23b7dcae6c79f4de83068" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "c4346d0257336219f1d26890", +"None": "c4346d0257336219f1d26890", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "c4346d0257336219f1d26890", +"duckdb": "c4346d0257336219f1d26890", +"generic": "c4346d0257336219f1d26890", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4346d0257336219f1d26890", +"postgres": "c4346d0257336219f1d26890", +"postgresql": "c4346d0257336219f1d26890", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "c4346d0257336219f1d26890" +} +], +[ +"type", +{ +"DuckDB": "bcc4b2c4a8f93d406cf00bc8", +"None": "cabeb00c4bc6c9e3d88a79ed", +"bigquery": "135a912ea74f76a35d9d0632", +"databricks": "cabeb00c4bc6c9e3d88a79ed", +"duckdb": "bcc4b2c4a8f93d406cf00bc8", +"generic": "cabeb00c4bc6c9e3d88a79ed", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "bcc4b2c4a8f93d406cf00bc8", +"postgresql": "bcc4b2c4a8f93d406cf00bc8", +"snowflake": "c4046288c6fec9a1344b0faa", +"tsql": "c523f494d181f495aec369d3" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "63c80b5d8e861216f0c45bfd", +"None": "aa04c66b4fed5190c90308fd", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "aa04c66b4fed5190c90308fd", +"duckdb": "63c80b5d8e861216f0c45bfd", +"generic": "aa04c66b4fed5190c90308fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aa04c66b4fed5190c90308fd", +"postgres": "aa04c66b4fed5190c90308fd", +"postgresql": "aa04c66b4fed5190c90308fd", +"snowflake": "d227b99a82e505d599c2ed8a", +"tsql": "aa04c66b4fed5190c90308fd" +} +], +[ +"type", +{ +"DuckDB": "fa07c8ec6ebf3228010ebd9a", +"None": "fa07c8ec6ebf3228010ebd9a", +"bigquery": "fa07c8ec6ebf3228010ebd9a", +"databricks": "fa07c8ec6ebf3228010ebd9a", +"duckdb": "fa07c8ec6ebf3228010ebd9a", +"generic": "fa07c8ec6ebf3228010ebd9a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fa07c8ec6ebf3228010ebd9a", +"postgres": "fa07c8ec6ebf3228010ebd9a", +"postgresql": "fa07c8ec6ebf3228010ebd9a", +"snowflake": "fa07c8ec6ebf3228010ebd9a", +"tsql": "fa07c8ec6ebf3228010ebd9a" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "226beccc1d430773f4e67b70", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "35e216b6921bf843994f2eea", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "35e216b6921bf843994f2eea", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "c2700ca3573352821e09af03", +"None": "c2700ca3573352821e09af03", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c2700ca3573352821e09af03", +"duckdb": "c2700ca3573352821e09af03", +"generic": "c2700ca3573352821e09af03", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c2700ca3573352821e09af03", +"postgres": "c2700ca3573352821e09af03", +"postgresql": "c2700ca3573352821e09af03", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e80839f7b11a9775b7596b50" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "4b27937f44f966ff9f3a7c76", +"None": "4b27937f44f966ff9f3a7c76", +"bigquery": "4b27937f44f966ff9f3a7c76", +"databricks": "4b27937f44f966ff9f3a7c76", +"duckdb": "4b27937f44f966ff9f3a7c76", +"generic": "4b27937f44f966ff9f3a7c76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b27937f44f966ff9f3a7c76", +"postgres": "4b27937f44f966ff9f3a7c76", +"postgresql": "4b27937f44f966ff9f3a7c76", +"snowflake": "4b27937f44f966ff9f3a7c76", +"tsql": "4b27937f44f966ff9f3a7c76" +} +], +[ +"type", +{ +"DuckDB": "d63e1b6839b94c8b42376477", +"None": "d63e1b6839b94c8b42376477", +"bigquery": "d63e1b6839b94c8b42376477", +"databricks": "d63e1b6839b94c8b42376477", +"duckdb": "d63e1b6839b94c8b42376477", +"generic": "d63e1b6839b94c8b42376477", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d63e1b6839b94c8b42376477", +"postgres": "d63e1b6839b94c8b42376477", +"postgresql": "d63e1b6839b94c8b42376477", +"snowflake": "d63e1b6839b94c8b42376477", +"tsql": "d63e1b6839b94c8b42376477" +} +], +[ +"type", +{ +"DuckDB": "c752fd9fe2ad3b86b8b899da", +"None": "c752fd9fe2ad3b86b8b899da", +"bigquery": "c752fd9fe2ad3b86b8b899da", +"databricks": "c752fd9fe2ad3b86b8b899da", +"duckdb": "c752fd9fe2ad3b86b8b899da", +"generic": "c752fd9fe2ad3b86b8b899da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c752fd9fe2ad3b86b8b899da", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c752fd9fe2ad3b86b8b899da" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "1e75e5227bea0ae294ac7af1", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "1e75e5227bea0ae294ac7af1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "19bdd92ea0cc5fb028657510", +"postgresql": "19bdd92ea0cc5fb028657510", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "901e8c830028411846541f65" +} +], +[ +"type", +{ +"DuckDB": "57922064a7dc546cbb50e42b", +"None": "57922064a7dc546cbb50e42b", +"bigquery": "57922064a7dc546cbb50e42b", +"databricks": "57922064a7dc546cbb50e42b", +"duckdb": "57922064a7dc546cbb50e42b", +"generic": "57922064a7dc546cbb50e42b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "57922064a7dc546cbb50e42b", +"postgres": "57922064a7dc546cbb50e42b", +"postgresql": "57922064a7dc546cbb50e42b", +"snowflake": "57922064a7dc546cbb50e42b", +"tsql": "57922064a7dc546cbb50e42b" +} +], +[ +"type", +{ +"DuckDB": "d4a375fcf7c3190951992469", +"None": "d4a375fcf7c3190951992469", +"bigquery": "5226d7e2a62c7650a87d6e84", +"databricks": "d4a375fcf7c3190951992469", +"duckdb": "d4a375fcf7c3190951992469", +"generic": "d4a375fcf7c3190951992469", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d4a375fcf7c3190951992469", +"postgres": "d4a375fcf7c3190951992469", +"postgresql": "d4a375fcf7c3190951992469", +"snowflake": "6306b9e88f7b13dbb6a32c00", +"tsql": "d4a375fcf7c3190951992469" +} +], +[ +"type", +{ +"DuckDB": "8698d724534891869075175e", +"None": "0017a88bb60ec3403c786ed8", +"bigquery": "47e0816fecdd9e015d7aeb79", +"databricks": "0017a88bb60ec3403c786ed8", +"duckdb": "8698d724534891869075175e", +"generic": "0017a88bb60ec3403c786ed8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8698d724534891869075175e", +"postgresql": "8698d724534891869075175e", +"snowflake": "e6f36041b1d9fb8fe3a18cbb", +"tsql": "364031fff119e6530478e415" +} +], +[ +"type", +{ +"DuckDB": "14c32cfb5e5135338d015f42", +"None": "14c32cfb5e5135338d015f42", +"bigquery": "14c32cfb5e5135338d015f42", +"databricks": "14c32cfb5e5135338d015f42", +"duckdb": "14c32cfb5e5135338d015f42", +"generic": "14c32cfb5e5135338d015f42", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "14c32cfb5e5135338d015f42", +"postgres": "14c32cfb5e5135338d015f42", +"postgresql": "14c32cfb5e5135338d015f42", +"snowflake": "14c32cfb5e5135338d015f42", +"tsql": "14c32cfb5e5135338d015f42" +} +], +[ +"type", +{ +"DuckDB": "73d31c6e78cdc3b595b9c4e9", +"None": "73d31c6e78cdc3b595b9c4e9", +"bigquery": "73d31c6e78cdc3b595b9c4e9", +"databricks": "73d31c6e78cdc3b595b9c4e9", +"duckdb": "73d31c6e78cdc3b595b9c4e9", +"generic": "73d31c6e78cdc3b595b9c4e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73d31c6e78cdc3b595b9c4e9", +"postgres": "73d31c6e78cdc3b595b9c4e9", +"postgresql": "73d31c6e78cdc3b595b9c4e9", +"snowflake": "73d31c6e78cdc3b595b9c4e9", +"tsql": "73d31c6e78cdc3b595b9c4e9" +} +], +[ +"type", +{ +"DuckDB": "6f763a99e9c69109526b40c0", +"None": "6f763a99e9c69109526b40c0", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "6f763a99e9c69109526b40c0", +"duckdb": "6f763a99e9c69109526b40c0", +"generic": "6f763a99e9c69109526b40c0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6f763a99e9c69109526b40c0", +"postgres": "6f763a99e9c69109526b40c0", +"postgresql": "6f763a99e9c69109526b40c0", +"snowflake": "2705f19942a4194a72576911", +"tsql": "6f763a99e9c69109526b40c0" +} +], +[ +"type", +{ +"DuckDB": "78b2cf275c640af54feeca31", +"None": "78b2cf275c640af54feeca31", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "78b2cf275c640af54feeca31", +"duckdb": "78b2cf275c640af54feeca31", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "78b2cf275c640af54feeca31", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "f78e07518606fbc42e0f28fe", +"None": "f78e07518606fbc42e0f28fe", +"bigquery": "f78e07518606fbc42e0f28fe", +"databricks": "f78e07518606fbc42e0f28fe", +"duckdb": "f78e07518606fbc42e0f28fe", +"generic": "f78e07518606fbc42e0f28fe", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f78e07518606fbc42e0f28fe", +"postgres": "f78e07518606fbc42e0f28fe", +"postgresql": "f78e07518606fbc42e0f28fe", +"snowflake": "f78e07518606fbc42e0f28fe", +"tsql": "f78e07518606fbc42e0f28fe" +} +], +[ +"type", +{ +"DuckDB": "11f5a5e0f332f3a87c8ff396", +"None": "11f5a5e0f332f3a87c8ff396", +"bigquery": "11f5a5e0f332f3a87c8ff396", +"databricks": "11f5a5e0f332f3a87c8ff396", +"duckdb": "11f5a5e0f332f3a87c8ff396", +"generic": "11f5a5e0f332f3a87c8ff396", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "11f5a5e0f332f3a87c8ff396", +"postgres": "11f5a5e0f332f3a87c8ff396", +"postgresql": "11f5a5e0f332f3a87c8ff396", +"snowflake": "11f5a5e0f332f3a87c8ff396", +"tsql": "11f5a5e0f332f3a87c8ff396" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "f20ec02865769c13aa2d2b67", +"None": "f20ec02865769c13aa2d2b67", +"bigquery": "8575088c2f44d4349e1307a4", +"databricks": "e40ec5056d50c25089ee3934", +"duckdb": "f20ec02865769c13aa2d2b67", +"generic": "f20ec02865769c13aa2d2b67", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f20ec02865769c13aa2d2b67", +"postgres": "f20ec02865769c13aa2d2b67", +"postgresql": "f20ec02865769c13aa2d2b67", +"snowflake": "e5502f3bf9aad4d98960c7b4", +"tsql": "f20ec02865769c13aa2d2b67" +} +], +[ +"type", +{ +"DuckDB": "d22967425ad0e5a048596ac6", +"None": "d22967425ad0e5a048596ac6", +"bigquery": "d22967425ad0e5a048596ac6", +"databricks": "d22967425ad0e5a048596ac6", +"duckdb": "d22967425ad0e5a048596ac6", +"generic": "d22967425ad0e5a048596ac6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d22967425ad0e5a048596ac6", +"postgres": "d22967425ad0e5a048596ac6", +"postgresql": "d22967425ad0e5a048596ac6", +"snowflake": "d22967425ad0e5a048596ac6", +"tsql": "d22967425ad0e5a048596ac6" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "eded69a48ff8476dbf73ff05", +"None": "eded69a48ff8476dbf73ff05", +"bigquery": "eded69a48ff8476dbf73ff05", +"databricks": "eded69a48ff8476dbf73ff05", +"duckdb": "eded69a48ff8476dbf73ff05", +"generic": "eded69a48ff8476dbf73ff05", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eded69a48ff8476dbf73ff05", +"postgres": "eded69a48ff8476dbf73ff05", +"postgresql": "eded69a48ff8476dbf73ff05", +"snowflake": "eded69a48ff8476dbf73ff05", +"tsql": "eded69a48ff8476dbf73ff05" +} +], +[ +"type", +{ +"DuckDB": "d7bedd3c7663f140f4d10bd4", +"None": "d7bedd3c7663f140f4d10bd4", +"bigquery": "d7bedd3c7663f140f4d10bd4", +"databricks": "d7bedd3c7663f140f4d10bd4", +"duckdb": "d7bedd3c7663f140f4d10bd4", +"generic": "d7bedd3c7663f140f4d10bd4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d7bedd3c7663f140f4d10bd4", +"postgres": "d7bedd3c7663f140f4d10bd4", +"postgresql": "d7bedd3c7663f140f4d10bd4", +"snowflake": "d7bedd3c7663f140f4d10bd4", +"tsql": "d7bedd3c7663f140f4d10bd4" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "c82815abe30b3d099903f838", +"None": "c82815abe30b3d099903f838", +"bigquery": "c82815abe30b3d099903f838", +"databricks": "c82815abe30b3d099903f838", +"duckdb": "c82815abe30b3d099903f838", +"generic": "c82815abe30b3d099903f838", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c82815abe30b3d099903f838", +"postgres": "c82815abe30b3d099903f838", +"postgresql": "c82815abe30b3d099903f838", +"snowflake": "c82815abe30b3d099903f838", +"tsql": "07ca53fdc57a22a78ad75787" +} +], +[ +"type", +{ +"DuckDB": "3739567e772c71d3b587ca23", +"None": "3739567e772c71d3b587ca23", +"bigquery": "3739567e772c71d3b587ca23", +"databricks": "3739567e772c71d3b587ca23", +"duckdb": "3739567e772c71d3b587ca23", +"generic": "3739567e772c71d3b587ca23", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3739567e772c71d3b587ca23", +"postgres": "3739567e772c71d3b587ca23", +"postgresql": "3739567e772c71d3b587ca23", +"snowflake": "3739567e772c71d3b587ca23", +"tsql": "3739567e772c71d3b587ca23" +} +], +[ +"type", +{ +"DuckDB": "83fdbd1f0a6ee4e72faa4d94", +"None": "83fdbd1f0a6ee4e72faa4d94", +"bigquery": "83fdbd1f0a6ee4e72faa4d94", +"databricks": "83fdbd1f0a6ee4e72faa4d94", +"duckdb": "83fdbd1f0a6ee4e72faa4d94", +"generic": "83fdbd1f0a6ee4e72faa4d94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "83fdbd1f0a6ee4e72faa4d94", +"postgres": "83fdbd1f0a6ee4e72faa4d94", +"postgresql": "83fdbd1f0a6ee4e72faa4d94", +"snowflake": "83fdbd1f0a6ee4e72faa4d94", +"tsql": "83fdbd1f0a6ee4e72faa4d94" +} +], +[ +"type", +{ +"DuckDB": "af57dd2575fa5a01917ec9cf", +"None": "af57dd2575fa5a01917ec9cf", +"bigquery": "af57dd2575fa5a01917ec9cf", +"databricks": "af57dd2575fa5a01917ec9cf", +"duckdb": "af57dd2575fa5a01917ec9cf", +"generic": "af57dd2575fa5a01917ec9cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "af57dd2575fa5a01917ec9cf", +"postgres": "af57dd2575fa5a01917ec9cf", +"postgresql": "af57dd2575fa5a01917ec9cf", +"snowflake": "af57dd2575fa5a01917ec9cf", +"tsql": "af57dd2575fa5a01917ec9cf" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "c0eec31620dfc2ac0028173d", +"None": "d3bcf10e00645b12a802c7d0", +"bigquery": "02e0b2ed25f32a8d09350a23", +"databricks": "d3bcf10e00645b12a802c7d0", +"duckdb": "c0eec31620dfc2ac0028173d", +"generic": "d3bcf10e00645b12a802c7d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "c0eec31620dfc2ac0028173d", +"postgresql": "c0eec31620dfc2ac0028173d", +"snowflake": "d379c0ee4dad89e570b7329e", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "39257e40f37cf05c780a4e22", +"None": "39257e40f37cf05c780a4e22", +"bigquery": "39257e40f37cf05c780a4e22", +"databricks": "39257e40f37cf05c780a4e22", +"duckdb": "39257e40f37cf05c780a4e22", +"generic": "39257e40f37cf05c780a4e22", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "39257e40f37cf05c780a4e22", +"postgres": "39257e40f37cf05c780a4e22", +"postgresql": "39257e40f37cf05c780a4e22", +"snowflake": "39257e40f37cf05c780a4e22", +"tsql": "39257e40f37cf05c780a4e22" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "251aba3fef503ce64556c701", +"postgresql": "251aba3fef503ce64556c701", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "251aba3fef503ce64556c701" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "251aba3fef503ce64556c701", +"postgresql": "251aba3fef503ce64556c701", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "251aba3fef503ce64556c701" +} +], +[ +"type", +{ +"DuckDB": "bfce65957f9ed1017daedf21", +"None": "bfce65957f9ed1017daedf21", +"bigquery": "a82a2bde913bf78a8d3f9332", +"databricks": "2d20c9c50b78d6e633cf256a", +"duckdb": "bfce65957f9ed1017daedf21", +"generic": "bfce65957f9ed1017daedf21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bfce65957f9ed1017daedf21", +"postgres": "bfce65957f9ed1017daedf21", +"postgresql": "bfce65957f9ed1017daedf21", +"snowflake": "2d20c9c50b78d6e633cf256a", +"tsql": "2d20c9c50b78d6e633cf256a" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "53ec6db7b2c81e2ceee285a3", +"None": "53ec6db7b2c81e2ceee285a3", +"bigquery": "53ec6db7b2c81e2ceee285a3", +"databricks": "53ec6db7b2c81e2ceee285a3", +"duckdb": "53ec6db7b2c81e2ceee285a3", +"generic": "53ec6db7b2c81e2ceee285a3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "53ec6db7b2c81e2ceee285a3", +"postgres": "53ec6db7b2c81e2ceee285a3", +"postgresql": "53ec6db7b2c81e2ceee285a3", +"snowflake": "53ec6db7b2c81e2ceee285a3", +"tsql": "53ec6db7b2c81e2ceee285a3" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "2427f5522575cdc3d6a74e4c", +"None": "2427f5522575cdc3d6a74e4c", +"bigquery": "2427f5522575cdc3d6a74e4c", +"databricks": "2427f5522575cdc3d6a74e4c", +"duckdb": "2427f5522575cdc3d6a74e4c", +"generic": "2427f5522575cdc3d6a74e4c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2427f5522575cdc3d6a74e4c", +"postgres": "2427f5522575cdc3d6a74e4c", +"postgresql": "2427f5522575cdc3d6a74e4c", +"snowflake": "2427f5522575cdc3d6a74e4c", +"tsql": "2427f5522575cdc3d6a74e4c" +} +], +[ +"type", +{ +"DuckDB": "158229f1c3f1085b80d3190e", +"None": "158229f1c3f1085b80d3190e", +"bigquery": "158229f1c3f1085b80d3190e", +"databricks": "158229f1c3f1085b80d3190e", +"duckdb": "158229f1c3f1085b80d3190e", +"generic": "158229f1c3f1085b80d3190e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "158229f1c3f1085b80d3190e", +"postgres": "158229f1c3f1085b80d3190e", +"postgresql": "158229f1c3f1085b80d3190e", +"snowflake": "158229f1c3f1085b80d3190e", +"tsql": "158229f1c3f1085b80d3190e" +} +], +[ +"type", +{ +"DuckDB": "e3bc1b8bad22d8104417b00f", +"None": "e3bc1b8bad22d8104417b00f", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "e3bc1b8bad22d8104417b00f", +"generic": "e3bc1b8bad22d8104417b00f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e3bc1b8bad22d8104417b00f", +"postgres": "111af35848fe500ec2ae54b2", +"postgresql": "111af35848fe500ec2ae54b2", +"snowflake": "e3bc1b8bad22d8104417b00f", +"tsql": "e3bc1b8bad22d8104417b00f" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "9bb8e9bdc03c6262fc0b887e", +"None": "9bb8e9bdc03c6262fc0b887e", +"bigquery": "9bb8e9bdc03c6262fc0b887e", +"databricks": "9bb8e9bdc03c6262fc0b887e", +"duckdb": "9bb8e9bdc03c6262fc0b887e", +"generic": "9bb8e9bdc03c6262fc0b887e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9bb8e9bdc03c6262fc0b887e", +"postgres": "9bb8e9bdc03c6262fc0b887e", +"postgresql": "9bb8e9bdc03c6262fc0b887e", +"snowflake": "9bb8e9bdc03c6262fc0b887e", +"tsql": "9bb8e9bdc03c6262fc0b887e" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "b69a57be16780e0a5e4acadb", +"None": "b69a57be16780e0a5e4acadb", +"bigquery": "b69a57be16780e0a5e4acadb", +"databricks": "b69a57be16780e0a5e4acadb", +"duckdb": "b69a57be16780e0a5e4acadb", +"generic": "b69a57be16780e0a5e4acadb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b69a57be16780e0a5e4acadb", +"postgres": "b69a57be16780e0a5e4acadb", +"postgresql": "b69a57be16780e0a5e4acadb", +"snowflake": "b69a57be16780e0a5e4acadb", +"tsql": "b69a57be16780e0a5e4acadb" +} +], +[ +"type", +{ +"DuckDB": "c703e456945a8f06d005c98b", +"None": "c703e456945a8f06d005c98b", +"bigquery": "c703e456945a8f06d005c98b", +"databricks": "c703e456945a8f06d005c98b", +"duckdb": "c703e456945a8f06d005c98b", +"generic": "c703e456945a8f06d005c98b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c703e456945a8f06d005c98b", +"postgres": "c703e456945a8f06d005c98b", +"postgresql": "c703e456945a8f06d005c98b", +"snowflake": "c703e456945a8f06d005c98b", +"tsql": "c703e456945a8f06d005c98b" +} +], +[ +"type", +{ +"DuckDB": "8371f7fd8d5afa0c7c86482b", +"None": "8371f7fd8d5afa0c7c86482b", +"bigquery": "8371f7fd8d5afa0c7c86482b", +"databricks": "8371f7fd8d5afa0c7c86482b", +"duckdb": "8371f7fd8d5afa0c7c86482b", +"generic": "8371f7fd8d5afa0c7c86482b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8371f7fd8d5afa0c7c86482b", +"postgres": "8371f7fd8d5afa0c7c86482b", +"postgresql": "8371f7fd8d5afa0c7c86482b", +"snowflake": "8371f7fd8d5afa0c7c86482b", +"tsql": "8371f7fd8d5afa0c7c86482b" +} +], +[ +"type", +{ +"DuckDB": "f4fc387af06ec14d0ed37c37", +"None": "f4fc387af06ec14d0ed37c37", +"bigquery": "f4fc387af06ec14d0ed37c37", +"databricks": "f4fc387af06ec14d0ed37c37", +"duckdb": "f4fc387af06ec14d0ed37c37", +"generic": "f4fc387af06ec14d0ed37c37", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4fc387af06ec14d0ed37c37", +"postgres": "f4fc387af06ec14d0ed37c37", +"postgresql": "f4fc387af06ec14d0ed37c37", +"snowflake": "f4fc387af06ec14d0ed37c37", +"tsql": "f4fc387af06ec14d0ed37c37" +} +], +[ +"type", +{ +"DuckDB": "97a92f4a47efe38678dac07f", +"None": "97a92f4a47efe38678dac07f", +"bigquery": "97a92f4a47efe38678dac07f", +"databricks": "97a92f4a47efe38678dac07f", +"duckdb": "97a92f4a47efe38678dac07f", +"generic": "97a92f4a47efe38678dac07f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "97a92f4a47efe38678dac07f", +"postgres": "97a92f4a47efe38678dac07f", +"postgresql": "97a92f4a47efe38678dac07f", +"snowflake": "97a92f4a47efe38678dac07f", +"tsql": "97a92f4a47efe38678dac07f" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "279ea7f6f6a3910de04b795b", +"None": "279ea7f6f6a3910de04b795b", +"bigquery": "279ea7f6f6a3910de04b795b", +"databricks": "279ea7f6f6a3910de04b795b", +"duckdb": "279ea7f6f6a3910de04b795b", +"generic": "279ea7f6f6a3910de04b795b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "279ea7f6f6a3910de04b795b", +"postgres": "279ea7f6f6a3910de04b795b", +"postgresql": "279ea7f6f6a3910de04b795b", +"snowflake": "279ea7f6f6a3910de04b795b", +"tsql": "279ea7f6f6a3910de04b795b" +} +], +[ +"type", +{ +"DuckDB": "04f0b885283ff5fc129874ad", +"None": "04f0b885283ff5fc129874ad", +"bigquery": "04f0b885283ff5fc129874ad", +"databricks": "04f0b885283ff5fc129874ad", +"duckdb": "04f0b885283ff5fc129874ad", +"generic": "04f0b885283ff5fc129874ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "04f0b885283ff5fc129874ad", +"postgres": "04f0b885283ff5fc129874ad", +"postgresql": "04f0b885283ff5fc129874ad", +"snowflake": "04f0b885283ff5fc129874ad", +"tsql": "04f0b885283ff5fc129874ad" +} +], +[ +"type", +{ +"DuckDB": "c6654395c62d21b415299f72", +"None": "c6654395c62d21b415299f72", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "c6654395c62d21b415299f72", +"duckdb": "c6654395c62d21b415299f72", +"generic": "c6654395c62d21b415299f72", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2978a8fe925c7f93c6e9fcfb", +"postgres": "c6654395c62d21b415299f72", +"postgresql": "c6654395c62d21b415299f72", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "c6654395c62d21b415299f72" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "93c1b5b5960e26479baf88d0", +"None": "93c1b5b5960e26479baf88d0", +"bigquery": "93c1b5b5960e26479baf88d0", +"databricks": "93c1b5b5960e26479baf88d0", +"duckdb": "93c1b5b5960e26479baf88d0", +"generic": "93c1b5b5960e26479baf88d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "93c1b5b5960e26479baf88d0", +"postgres": "93c1b5b5960e26479baf88d0", +"postgresql": "93c1b5b5960e26479baf88d0", +"snowflake": "93c1b5b5960e26479baf88d0", +"tsql": "93c1b5b5960e26479baf88d0" +} +], +[ +"type", +{ +"DuckDB": "6a912271e85e6d85c1238bc0", +"None": "6a912271e85e6d85c1238bc0", +"bigquery": "6a912271e85e6d85c1238bc0", +"databricks": "6a912271e85e6d85c1238bc0", +"duckdb": "6a912271e85e6d85c1238bc0", +"generic": "6a912271e85e6d85c1238bc0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a912271e85e6d85c1238bc0", +"postgres": "6a912271e85e6d85c1238bc0", +"postgresql": "6a912271e85e6d85c1238bc0", +"snowflake": "6a912271e85e6d85c1238bc0", +"tsql": "6a912271e85e6d85c1238bc0" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "eea9a25a6fbf8e049a445513", +"None": "eea9a25a6fbf8e049a445513", +"bigquery": "eea9a25a6fbf8e049a445513", +"databricks": "eea9a25a6fbf8e049a445513", +"duckdb": "eea9a25a6fbf8e049a445513", +"generic": "eea9a25a6fbf8e049a445513", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eea9a25a6fbf8e049a445513", +"postgres": "eea9a25a6fbf8e049a445513", +"postgresql": "eea9a25a6fbf8e049a445513", +"snowflake": "eea9a25a6fbf8e049a445513", +"tsql": "eea9a25a6fbf8e049a445513" +} +], +[ +"type", +{ +"DuckDB": "304be1d51ad99bfab3750f57", +"None": "304be1d51ad99bfab3750f57", +"bigquery": "304be1d51ad99bfab3750f57", +"databricks": "304be1d51ad99bfab3750f57", +"duckdb": "304be1d51ad99bfab3750f57", +"generic": "304be1d51ad99bfab3750f57", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "304be1d51ad99bfab3750f57", +"postgres": "304be1d51ad99bfab3750f57", +"postgresql": "304be1d51ad99bfab3750f57", +"snowflake": "304be1d51ad99bfab3750f57", +"tsql": "304be1d51ad99bfab3750f57" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "5d0a19a893dbf0c1e6e9f15c", +"None": "5d0a19a893dbf0c1e6e9f15c", +"bigquery": "5d0a19a893dbf0c1e6e9f15c", +"databricks": "5d0a19a893dbf0c1e6e9f15c", +"duckdb": "5d0a19a893dbf0c1e6e9f15c", +"generic": "5d0a19a893dbf0c1e6e9f15c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5d0a19a893dbf0c1e6e9f15c", +"postgres": "5d0a19a893dbf0c1e6e9f15c", +"postgresql": "5d0a19a893dbf0c1e6e9f15c", +"snowflake": "5d0a19a893dbf0c1e6e9f15c", +"tsql": "5d0a19a893dbf0c1e6e9f15c" +} +], +[ +"type", +{ +"DuckDB": "9bb8e9bdc03c6262fc0b887e", +"None": "9bb8e9bdc03c6262fc0b887e", +"bigquery": "9bb8e9bdc03c6262fc0b887e", +"databricks": "9bb8e9bdc03c6262fc0b887e", +"duckdb": "9bb8e9bdc03c6262fc0b887e", +"generic": "9bb8e9bdc03c6262fc0b887e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9bb8e9bdc03c6262fc0b887e", +"postgres": "9bb8e9bdc03c6262fc0b887e", +"postgresql": "9bb8e9bdc03c6262fc0b887e", +"snowflake": "9bb8e9bdc03c6262fc0b887e", +"tsql": "9bb8e9bdc03c6262fc0b887e" +} +], +[ +"type", +{ +"DuckDB": "15f0b4e032d5524678c61cae", +"None": "15f0b4e032d5524678c61cae", +"bigquery": "15f0b4e032d5524678c61cae", +"databricks": "15f0b4e032d5524678c61cae", +"duckdb": "15f0b4e032d5524678c61cae", +"generic": "15f0b4e032d5524678c61cae", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "15f0b4e032d5524678c61cae", +"postgres": "15f0b4e032d5524678c61cae", +"postgresql": "15f0b4e032d5524678c61cae", +"snowflake": "15f0b4e032d5524678c61cae", +"tsql": "15f0b4e032d5524678c61cae" +} +], +[ +"type", +{ +"DuckDB": "cafb5cff5526dd419baad0ed", +"None": "cafb5cff5526dd419baad0ed", +"bigquery": "cafb5cff5526dd419baad0ed", +"databricks": "cafb5cff5526dd419baad0ed", +"duckdb": "cafb5cff5526dd419baad0ed", +"generic": "cafb5cff5526dd419baad0ed", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cafb5cff5526dd419baad0ed", +"postgres": "cafb5cff5526dd419baad0ed", +"postgresql": "cafb5cff5526dd419baad0ed", +"snowflake": "cafb5cff5526dd419baad0ed", +"tsql": "cafb5cff5526dd419baad0ed" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "bf5a83d0c02f8890941c7ec8", +"None": "bf5a83d0c02f8890941c7ec8", +"bigquery": "bf5a83d0c02f8890941c7ec8", +"databricks": "bf5a83d0c02f8890941c7ec8", +"duckdb": "bf5a83d0c02f8890941c7ec8", +"generic": "bf5a83d0c02f8890941c7ec8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bf5a83d0c02f8890941c7ec8", +"postgres": "bf5a83d0c02f8890941c7ec8", +"postgresql": "bf5a83d0c02f8890941c7ec8", +"snowflake": "bf5a83d0c02f8890941c7ec8", +"tsql": "bf5a83d0c02f8890941c7ec8" +} +], +[ +"type", +{ +"DuckDB": "4c16382f7ad617ac3cbfa833", +"None": "0f63c150afc0bb5bbf7763ba", +"bigquery": "a4d165ed06d0b1b815192ef4", +"databricks": "0f63c150afc0bb5bbf7763ba", +"duckdb": "4c16382f7ad617ac3cbfa833", +"generic": "0f63c150afc0bb5bbf7763ba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "4c16382f7ad617ac3cbfa833", +"postgresql": "4c16382f7ad617ac3cbfa833", +"snowflake": "303d6e0feaedc557ea64e414", +"tsql": "accf46359aa8c74483b9a197" +} +], +[ +"type", +{ +"DuckDB": "d9315b336b73c3d8d4b6e9a3", +"None": "f2ed52f6bf46b4db9343551c", +"bigquery": "f2ed52f6bf46b4db9343551c", +"databricks": "f2ed52f6bf46b4db9343551c", +"duckdb": "d9315b336b73c3d8d4b6e9a3", +"generic": "f2ed52f6bf46b4db9343551c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f2ed52f6bf46b4db9343551c", +"postgres": "f2ed52f6bf46b4db9343551c", +"postgresql": "f2ed52f6bf46b4db9343551c", +"snowflake": "f2ed52f6bf46b4db9343551c", +"tsql": "f2ed52f6bf46b4db9343551c" +} +], +[ +"type", +{ +"DuckDB": "c84c0b1d4d7caa6744130456", +"None": "c84c0b1d4d7caa6744130456", +"bigquery": "c84c0b1d4d7caa6744130456", +"databricks": "c84c0b1d4d7caa6744130456", +"duckdb": "c84c0b1d4d7caa6744130456", +"generic": "c84c0b1d4d7caa6744130456", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c84c0b1d4d7caa6744130456", +"postgres": "c84c0b1d4d7caa6744130456", +"postgresql": "c84c0b1d4d7caa6744130456", +"snowflake": "c84c0b1d4d7caa6744130456", +"tsql": "c84c0b1d4d7caa6744130456" +} +], +[ +"type", +{ +"DuckDB": "aea8a680fa18770e56bf6220", +"None": "aea8a680fa18770e56bf6220", +"bigquery": "aea8a680fa18770e56bf6220", +"databricks": "aea8a680fa18770e56bf6220", +"duckdb": "aea8a680fa18770e56bf6220", +"generic": "aea8a680fa18770e56bf6220", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aea8a680fa18770e56bf6220", +"postgres": "aea8a680fa18770e56bf6220", +"postgresql": "aea8a680fa18770e56bf6220", +"snowflake": "aea8a680fa18770e56bf6220", +"tsql": "aea8a680fa18770e56bf6220" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "5aa51d84ac5e6c7a9306f348", +"bigquery": "5aa51d84ac5e6c7a9306f348", +"databricks": "5aa51d84ac5e6c7a9306f348", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "5aa51d84ac5e6c7a9306f348", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "c7b697bbfa82dd2821ad64e9", +"None": "c7b697bbfa82dd2821ad64e9", +"bigquery": "dcb00b7053ffe69fe3ce99cd", +"databricks": "dcb00b7053ffe69fe3ce99cd", +"duckdb": "c7b697bbfa82dd2821ad64e9", +"generic": "c7b697bbfa82dd2821ad64e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dcb00b7053ffe69fe3ce99cd", +"postgres": "c7b697bbfa82dd2821ad64e9", +"postgresql": "c7b697bbfa82dd2821ad64e9", +"snowflake": "c7b697bbfa82dd2821ad64e9", +"tsql": "c7b697bbfa82dd2821ad64e9" +} +], +[ +"type", +{ +"DuckDB": "c0b12bfc586282ce5ea85c45", +"None": "c0b12bfc586282ce5ea85c45", +"bigquery": "c0b12bfc586282ce5ea85c45", +"databricks": "c0b12bfc586282ce5ea85c45", +"duckdb": "c0b12bfc586282ce5ea85c45", +"generic": "c0b12bfc586282ce5ea85c45", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c0b12bfc586282ce5ea85c45", +"postgres": "c0b12bfc586282ce5ea85c45", +"postgresql": "c0b12bfc586282ce5ea85c45", +"snowflake": "c0b12bfc586282ce5ea85c45", +"tsql": "c0b12bfc586282ce5ea85c45" +} +], +[ +"type", +{ +"DuckDB": "806bf4b6d83b5dc9948e2054", +"None": "806bf4b6d83b5dc9948e2054", +"bigquery": "806bf4b6d83b5dc9948e2054", +"databricks": "806bf4b6d83b5dc9948e2054", +"duckdb": "806bf4b6d83b5dc9948e2054", +"generic": "806bf4b6d83b5dc9948e2054", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "806bf4b6d83b5dc9948e2054", +"postgres": "806bf4b6d83b5dc9948e2054", +"postgresql": "806bf4b6d83b5dc9948e2054", +"snowflake": "806bf4b6d83b5dc9948e2054", +"tsql": "806bf4b6d83b5dc9948e2054" +} +], +[ +"type", +{ +"DuckDB": "871969531da28b86361e65d2", +"None": "871969531da28b86361e65d2", +"bigquery": "871969531da28b86361e65d2", +"databricks": "871969531da28b86361e65d2", +"duckdb": "871969531da28b86361e65d2", +"generic": "871969531da28b86361e65d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "871969531da28b86361e65d2", +"postgres": "871969531da28b86361e65d2", +"postgresql": "871969531da28b86361e65d2", +"snowflake": "871969531da28b86361e65d2", +"tsql": "871969531da28b86361e65d2" +} +], +[ +"type", +{ +"DuckDB": "4129901e0f313b38452e22af", +"None": "4129901e0f313b38452e22af", +"bigquery": "4129901e0f313b38452e22af", +"databricks": "4129901e0f313b38452e22af", +"duckdb": "4129901e0f313b38452e22af", +"generic": "4129901e0f313b38452e22af", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4129901e0f313b38452e22af", +"postgres": "4129901e0f313b38452e22af", +"postgresql": "4129901e0f313b38452e22af", +"snowflake": "4129901e0f313b38452e22af", +"tsql": "4129901e0f313b38452e22af" +} +], +[ +"type", +{ +"DuckDB": "0c290c40483b9651ff557830", +"None": "0c290c40483b9651ff557830", +"bigquery": "0c290c40483b9651ff557830", +"databricks": "0c290c40483b9651ff557830", +"duckdb": "0c290c40483b9651ff557830", +"generic": "0c290c40483b9651ff557830", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c290c40483b9651ff557830", +"postgres": "0c290c40483b9651ff557830", +"postgresql": "0c290c40483b9651ff557830", +"snowflake": "0c290c40483b9651ff557830", +"tsql": "0c290c40483b9651ff557830" +} +], +[ +"type", +{ +"DuckDB": "b0505024b3f15f191c70a470", +"None": "b1603277660bbe23982ff72a", +"bigquery": "b1603277660bbe23982ff72a", +"databricks": "b1603277660bbe23982ff72a", +"duckdb": "b0505024b3f15f191c70a470", +"generic": "b1603277660bbe23982ff72a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "fa1a8787c8f69b9f9be89526", +"postgresql": "fa1a8787c8f69b9f9be89526", +"snowflake": "50404426183626ce7b686f80", +"tsql": "23410065fbf92eb4099b13de" +} +], +[ +"type", +{ +"DuckDB": "b63494be71c6c8936f6824a6", +"None": "b63494be71c6c8936f6824a6", +"bigquery": "b63494be71c6c8936f6824a6", +"databricks": "b63494be71c6c8936f6824a6", +"duckdb": "b63494be71c6c8936f6824a6", +"generic": "b63494be71c6c8936f6824a6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b63494be71c6c8936f6824a6", +"postgres": "b63494be71c6c8936f6824a6", +"postgresql": "b63494be71c6c8936f6824a6", +"snowflake": "b63494be71c6c8936f6824a6", +"tsql": "b63494be71c6c8936f6824a6" +} +], +[ +"type", +{ +"DuckDB": "016c25f8f0c58947f8da9152", +"None": "016c25f8f0c58947f8da9152", +"bigquery": "016c25f8f0c58947f8da9152", +"databricks": "016c25f8f0c58947f8da9152", +"duckdb": "016c25f8f0c58947f8da9152", +"generic": "016c25f8f0c58947f8da9152", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "016c25f8f0c58947f8da9152", +"postgres": "016c25f8f0c58947f8da9152", +"postgresql": "016c25f8f0c58947f8da9152", +"snowflake": "016c25f8f0c58947f8da9152", +"tsql": "016c25f8f0c58947f8da9152" +} +], +[ +"type", +{ +"DuckDB": "e02779be57c0fcfa69ee7746", +"None": "e02779be57c0fcfa69ee7746", +"bigquery": "e02779be57c0fcfa69ee7746", +"databricks": "e02779be57c0fcfa69ee7746", +"duckdb": "e02779be57c0fcfa69ee7746", +"generic": "e02779be57c0fcfa69ee7746", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e02779be57c0fcfa69ee7746", +"postgres": "e02779be57c0fcfa69ee7746", +"postgresql": "e02779be57c0fcfa69ee7746", +"snowflake": "e02779be57c0fcfa69ee7746", +"tsql": "e02779be57c0fcfa69ee7746" +} +], +[ +"type", +{ +"DuckDB": "ace891b6844cb59efad42031", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "ace891b6844cb59efad42031", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "b7507b03866507106687a313", +"None": "b7507b03866507106687a313", +"bigquery": "b7507b03866507106687a313", +"databricks": "b7507b03866507106687a313", +"duckdb": "b7507b03866507106687a313", +"generic": "b7507b03866507106687a313", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b7507b03866507106687a313", +"postgres": "b7507b03866507106687a313", +"postgresql": "b7507b03866507106687a313", +"snowflake": "b7507b03866507106687a313", +"tsql": "b7507b03866507106687a313" +} +], +[ +"type", +{ +"DuckDB": "fdc9a2fc03f4b67d24982d0a", +"None": "fdc9a2fc03f4b67d24982d0a", +"bigquery": "fdc9a2fc03f4b67d24982d0a", +"databricks": "fdc9a2fc03f4b67d24982d0a", +"duckdb": "fdc9a2fc03f4b67d24982d0a", +"generic": "fdc9a2fc03f4b67d24982d0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fdc9a2fc03f4b67d24982d0a", +"postgres": "fdc9a2fc03f4b67d24982d0a", +"postgresql": "fdc9a2fc03f4b67d24982d0a", +"snowflake": "fdc9a2fc03f4b67d24982d0a", +"tsql": "fdc9a2fc03f4b67d24982d0a" +} +], +[ +"type", +{ +"DuckDB": "5c8b98f516cedefc237fa721", +"None": "5c8b98f516cedefc237fa721", +"bigquery": "5c8b98f516cedefc237fa721", +"databricks": "5c8b98f516cedefc237fa721", +"duckdb": "5c8b98f516cedefc237fa721", +"generic": "5c8b98f516cedefc237fa721", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c8b98f516cedefc237fa721", +"postgres": "5c8b98f516cedefc237fa721", +"postgresql": "5c8b98f516cedefc237fa721", +"snowflake": "5c8b98f516cedefc237fa721", +"tsql": "5c8b98f516cedefc237fa721" +} +], +[ +"type", +{ +"DuckDB": "27d7e27586eab431e308413f", +"None": "8d26a44bbe0d29bdb1509369", +"bigquery": "8d26a44bbe0d29bdb1509369", +"databricks": "8d26a44bbe0d29bdb1509369", +"duckdb": "27d7e27586eab431e308413f", +"generic": "8d26a44bbe0d29bdb1509369", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8d26a44bbe0d29bdb1509369", +"postgres": "8d26a44bbe0d29bdb1509369", +"postgresql": "8d26a44bbe0d29bdb1509369", +"snowflake": "8d26a44bbe0d29bdb1509369", +"tsql": "8d26a44bbe0d29bdb1509369" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "4662a1758442ce44b830269f", +"None": "4662a1758442ce44b830269f", +"bigquery": "4662a1758442ce44b830269f", +"databricks": "4662a1758442ce44b830269f", +"duckdb": "4662a1758442ce44b830269f", +"generic": "4662a1758442ce44b830269f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4662a1758442ce44b830269f", +"postgres": "4662a1758442ce44b830269f", +"postgresql": "4662a1758442ce44b830269f", +"snowflake": "4662a1758442ce44b830269f", +"tsql": "4662a1758442ce44b830269f" +} +], +[ +"type", +{ +"DuckDB": "34af912a178427fc6e8bdee3", +"None": "34af912a178427fc6e8bdee3", +"bigquery": "34af912a178427fc6e8bdee3", +"databricks": "34af912a178427fc6e8bdee3", +"duckdb": "34af912a178427fc6e8bdee3", +"generic": "34af912a178427fc6e8bdee3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "34af912a178427fc6e8bdee3", +"postgres": "34af912a178427fc6e8bdee3", +"postgresql": "34af912a178427fc6e8bdee3", +"snowflake": "34af912a178427fc6e8bdee3", +"tsql": "34af912a178427fc6e8bdee3" +} +], +[ +"type", +{ +"DuckDB": "27d7e27586eab431e308413f", +"None": "29e896ae6f2844ec3744aad6", +"bigquery": "29e896ae6f2844ec3744aad6", +"databricks": "29e896ae6f2844ec3744aad6", +"duckdb": "27d7e27586eab431e308413f", +"generic": "29e896ae6f2844ec3744aad6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "29e896ae6f2844ec3744aad6", +"postgres": "29e896ae6f2844ec3744aad6", +"postgresql": "29e896ae6f2844ec3744aad6", +"snowflake": "29e896ae6f2844ec3744aad6", +"tsql": "29e896ae6f2844ec3744aad6" +} +], +[ +"type", +{ +"DuckDB": "e8605878c6fffc044556a858", +"None": "e8605878c6fffc044556a858", +"bigquery": "e8605878c6fffc044556a858", +"databricks": "e8605878c6fffc044556a858", +"duckdb": "e8605878c6fffc044556a858", +"generic": "e8605878c6fffc044556a858", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8605878c6fffc044556a858", +"postgres": "e8605878c6fffc044556a858", +"postgresql": "e8605878c6fffc044556a858", +"snowflake": "e8605878c6fffc044556a858", +"tsql": "e8605878c6fffc044556a858" +} +], +[ +"type", +{ +"DuckDB": "787213ce6b6e210ded1d752c", +"None": "787213ce6b6e210ded1d752c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "787213ce6b6e210ded1d752c", +"duckdb": "787213ce6b6e210ded1d752c", +"generic": "787213ce6b6e210ded1d752c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "787213ce6b6e210ded1d752c", +"postgres": "787213ce6b6e210ded1d752c", +"postgresql": "787213ce6b6e210ded1d752c", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "787213ce6b6e210ded1d752c" +} +], +[ +"type", +{ +"DuckDB": "c6106f30f23f392e17eadef4", +"None": "c6106f30f23f392e17eadef4", +"bigquery": "c6106f30f23f392e17eadef4", +"databricks": "c6106f30f23f392e17eadef4", +"duckdb": "c6106f30f23f392e17eadef4", +"generic": "c6106f30f23f392e17eadef4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6106f30f23f392e17eadef4", +"postgres": "c6106f30f23f392e17eadef4", +"postgresql": "c6106f30f23f392e17eadef4", +"snowflake": "c6106f30f23f392e17eadef4", +"tsql": "c6106f30f23f392e17eadef4" +} +], +[ +"type", +{ +"DuckDB": "e7aec8198ab628519324444a", +"None": "e7aec8198ab628519324444a", +"bigquery": "e7aec8198ab628519324444a", +"databricks": "e7aec8198ab628519324444a", +"duckdb": "e7aec8198ab628519324444a", +"generic": "e7aec8198ab628519324444a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7aec8198ab628519324444a", +"postgres": "e7aec8198ab628519324444a", +"postgresql": "e7aec8198ab628519324444a", +"snowflake": "e7aec8198ab628519324444a", +"tsql": "e7aec8198ab628519324444a" +} +], +[ +"type", +{ +"DuckDB": "881106a4a0d2de0ff7c56373", +"None": "881106a4a0d2de0ff7c56373", +"bigquery": "881106a4a0d2de0ff7c56373", +"databricks": "881106a4a0d2de0ff7c56373", +"duckdb": "881106a4a0d2de0ff7c56373", +"generic": "881106a4a0d2de0ff7c56373", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "881106a4a0d2de0ff7c56373", +"postgres": "881106a4a0d2de0ff7c56373", +"postgresql": "881106a4a0d2de0ff7c56373", +"snowflake": "881106a4a0d2de0ff7c56373", +"tsql": "881106a4a0d2de0ff7c56373" +} +], +[ +"type", +{ +"DuckDB": "2c6b371b030464f525066893", +"None": "2c6b371b030464f525066893", +"bigquery": "2c6b371b030464f525066893", +"databricks": "2c6b371b030464f525066893", +"duckdb": "2c6b371b030464f525066893", +"generic": "2c6b371b030464f525066893", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c6b371b030464f525066893", +"postgres": "2c6b371b030464f525066893", +"postgresql": "2c6b371b030464f525066893", +"snowflake": "2c6b371b030464f525066893", +"tsql": "2c6b371b030464f525066893" +} +], +[ +"type", +{ +"DuckDB": "e3d9a113277b9c69432b32d1", +"None": "e3d9a113277b9c69432b32d1", +"bigquery": "e3d9a113277b9c69432b32d1", +"databricks": "4cda99191af9adfbe91e70f6", +"duckdb": "e3d9a113277b9c69432b32d1", +"generic": "e3d9a113277b9c69432b32d1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4cda99191af9adfbe91e70f6", +"postgres": "e3d9a113277b9c69432b32d1", +"postgresql": "e3d9a113277b9c69432b32d1", +"snowflake": "e3d9a113277b9c69432b32d1", +"tsql": "e3d9a113277b9c69432b32d1" +} +], +[ +"type", +{ +"DuckDB": "ba0da7ef018b990fcc83cba1", +"None": "ba0da7ef018b990fcc83cba1", +"bigquery": "ba0da7ef018b990fcc83cba1", +"databricks": "ba0da7ef018b990fcc83cba1", +"duckdb": "ba0da7ef018b990fcc83cba1", +"generic": "ba0da7ef018b990fcc83cba1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ba0da7ef018b990fcc83cba1", +"postgres": "ba0da7ef018b990fcc83cba1", +"postgresql": "ba0da7ef018b990fcc83cba1", +"snowflake": "ba0da7ef018b990fcc83cba1", +"tsql": "ba0da7ef018b990fcc83cba1" +} +], +[ +"type", +{ +"DuckDB": "396b2ce32efb5365b393c230", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "396b2ce32efb5365b393c230", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "e060b5a5a8e9774b580d58d3", +"None": "e060b5a5a8e9774b580d58d3", +"bigquery": "319a9fbb02ed503eb114164b", +"databricks": "319a9fbb02ed503eb114164b", +"duckdb": "e060b5a5a8e9774b580d58d3", +"generic": "e060b5a5a8e9774b580d58d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "319a9fbb02ed503eb114164b", +"postgres": "e060b5a5a8e9774b580d58d3", +"postgresql": "e060b5a5a8e9774b580d58d3", +"snowflake": "e060b5a5a8e9774b580d58d3", +"tsql": "e060b5a5a8e9774b580d58d3" +} +], +[ +"type", +{ +"DuckDB": "67d251c042daee0bb2bf7351", +"None": "67d251c042daee0bb2bf7351", +"bigquery": "67d251c042daee0bb2bf7351", +"databricks": "67d251c042daee0bb2bf7351", +"duckdb": "67d251c042daee0bb2bf7351", +"generic": "67d251c042daee0bb2bf7351", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "67d251c042daee0bb2bf7351", +"postgres": "67d251c042daee0bb2bf7351", +"postgresql": "67d251c042daee0bb2bf7351", +"snowflake": "67d251c042daee0bb2bf7351", +"tsql": "67d251c042daee0bb2bf7351" +} +], +[ +"type", +{ +"DuckDB": "919b7ec6a7b49d8527b784cd", +"None": "919b7ec6a7b49d8527b784cd", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "919b7ec6a7b49d8527b784cd", +"generic": "919b7ec6a7b49d8527b784cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "919b7ec6a7b49d8527b784cd" +} +], +[ +"type", +{ +"DuckDB": "66ee8490513ef7b131cb4824", +"None": "66ee8490513ef7b131cb4824", +"bigquery": "66ee8490513ef7b131cb4824", +"databricks": "66ee8490513ef7b131cb4824", +"duckdb": "66ee8490513ef7b131cb4824", +"generic": "66ee8490513ef7b131cb4824", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "66ee8490513ef7b131cb4824", +"postgres": "66ee8490513ef7b131cb4824", +"postgresql": "66ee8490513ef7b131cb4824", +"snowflake": "66ee8490513ef7b131cb4824", +"tsql": "66ee8490513ef7b131cb4824" +} +], +[ +"type", +{ +"DuckDB": "8f552eefca3955a73672f686", +"None": "8f552eefca3955a73672f686", +"bigquery": "8f552eefca3955a73672f686", +"databricks": "8f552eefca3955a73672f686", +"duckdb": "8f552eefca3955a73672f686", +"generic": "8f552eefca3955a73672f686", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8f552eefca3955a73672f686", +"postgres": "8f552eefca3955a73672f686", +"postgresql": "8f552eefca3955a73672f686", +"snowflake": "8f552eefca3955a73672f686", +"tsql": "8f552eefca3955a73672f686" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "f4c37736a388a1d793d4de1a", +"None": "853d4786e4edb4f90284765b", +"bigquery": "853d4786e4edb4f90284765b", +"databricks": "853d4786e4edb4f90284765b", +"duckdb": "f4c37736a388a1d793d4de1a", +"generic": "853d4786e4edb4f90284765b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "f4c37736a388a1d793d4de1a", +"postgresql": "f4c37736a388a1d793d4de1a", +"snowflake": "a640dde3e800eb6ea27813e3", +"tsql": "640c6d24f5ef62763025ea43" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "776e4bf23c8ef10ffaceade2", +"None": "776e4bf23c8ef10ffaceade2", +"bigquery": "776e4bf23c8ef10ffaceade2", +"databricks": "776e4bf23c8ef10ffaceade2", +"duckdb": "776e4bf23c8ef10ffaceade2", +"generic": "776e4bf23c8ef10ffaceade2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "776e4bf23c8ef10ffaceade2", +"postgres": "776e4bf23c8ef10ffaceade2", +"postgresql": "776e4bf23c8ef10ffaceade2", +"snowflake": "776e4bf23c8ef10ffaceade2", +"tsql": "776e4bf23c8ef10ffaceade2" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "42d4a23d9a52e79d0c3458d3", +"None": "a3acd7ba35e81a800f967bd3", +"bigquery": "3c0719ca5c83e0a20d0b0b42", +"databricks": "a3acd7ba35e81a800f967bd3", +"duckdb": "42d4a23d9a52e79d0c3458d3", +"generic": "a3acd7ba35e81a800f967bd3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a3acd7ba35e81a800f967bd3", +"postgres": "a3acd7ba35e81a800f967bd3", +"postgresql": "a3acd7ba35e81a800f967bd3", +"snowflake": "a3acd7ba35e81a800f967bd3", +"tsql": "a3acd7ba35e81a800f967bd3" +} +], +[ +"type", +{ +"DuckDB": "95050a5e73c2bff179a785b7", +"None": "95050a5e73c2bff179a785b7", +"bigquery": "95050a5e73c2bff179a785b7", +"databricks": "95050a5e73c2bff179a785b7", +"duckdb": "95050a5e73c2bff179a785b7", +"generic": "95050a5e73c2bff179a785b7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "95050a5e73c2bff179a785b7", +"postgres": "95050a5e73c2bff179a785b7", +"postgresql": "95050a5e73c2bff179a785b7", +"snowflake": "95050a5e73c2bff179a785b7", +"tsql": "95050a5e73c2bff179a785b7" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "89cc3662f602a13aef39eb80", +"None": "f2bbf59a9c02f59ca89e66b6", +"bigquery": "f2bbf59a9c02f59ca89e66b6", +"databricks": "f2bbf59a9c02f59ca89e66b6", +"duckdb": "89cc3662f602a13aef39eb80", +"generic": "f2bbf59a9c02f59ca89e66b6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "89cc3662f602a13aef39eb80", +"postgresql": "89cc3662f602a13aef39eb80", +"snowflake": "6f88ef12d95c26bf8f0bd9e5", +"tsql": "ffadc5a4dbb5d511f5cde8d7" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "50a940812c6ed2e6dd842e74", +"None": "50a940812c6ed2e6dd842e74", +"bigquery": "50a940812c6ed2e6dd842e74", +"databricks": "50a940812c6ed2e6dd842e74", +"duckdb": "50a940812c6ed2e6dd842e74", +"generic": "50a940812c6ed2e6dd842e74", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "50a940812c6ed2e6dd842e74", +"postgres": "50a940812c6ed2e6dd842e74", +"postgresql": "50a940812c6ed2e6dd842e74", +"snowflake": "50a940812c6ed2e6dd842e74", +"tsql": "50a940812c6ed2e6dd842e74" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "279ea7f6f6a3910de04b795b", +"None": "279ea7f6f6a3910de04b795b", +"bigquery": "279ea7f6f6a3910de04b795b", +"databricks": "279ea7f6f6a3910de04b795b", +"duckdb": "279ea7f6f6a3910de04b795b", +"generic": "279ea7f6f6a3910de04b795b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "279ea7f6f6a3910de04b795b", +"postgres": "279ea7f6f6a3910de04b795b", +"postgresql": "279ea7f6f6a3910de04b795b", +"snowflake": "279ea7f6f6a3910de04b795b", +"tsql": "279ea7f6f6a3910de04b795b" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "84fca9ac893777959cfd715c", +"None": "84fca9ac893777959cfd715c", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "84fca9ac893777959cfd715c", +"duckdb": "84fca9ac893777959cfd715c", +"generic": "84fca9ac893777959cfd715c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "84fca9ac893777959cfd715c", +"postgres": "84fca9ac893777959cfd715c", +"postgresql": "84fca9ac893777959cfd715c", +"snowflake": "84fca9ac893777959cfd715c", +"tsql": "6349bc82604b762638e35788" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "2cc8493833d5ac6046bfbfc2", +"None": "2cc8493833d5ac6046bfbfc2", +"bigquery": "c6c57a853058ab1ee17a75f6", +"databricks": "75a99436882f66b2fdc219e5", +"duckdb": "2cc8493833d5ac6046bfbfc2", +"generic": "2cc8493833d5ac6046bfbfc2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2cc8493833d5ac6046bfbfc2", +"postgres": "2cc8493833d5ac6046bfbfc2", +"postgresql": "2cc8493833d5ac6046bfbfc2", +"snowflake": "70fb65041d155a676d8443d4", +"tsql": "c17d7423230bbb223a42e69f" +} +], +[ +"type", +{ +"DuckDB": "8305d75697b54f509bbc0f53", +"None": "80723d96ed6ed21034faa9bd", +"bigquery": "f80980deec760e5b50e05376", +"databricks": "f80980deec760e5b50e05376", +"duckdb": "8305d75697b54f509bbc0f53", +"generic": "80723d96ed6ed21034faa9bd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f80980deec760e5b50e05376", +"postgres": "8305d75697b54f509bbc0f53", +"postgresql": "8305d75697b54f509bbc0f53", +"snowflake": "240e7de80b095b23fdb1fd92", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "d41d40c68507f4d02e348833", +"None": "d41d40c68507f4d02e348833", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "d41d40c68507f4d02e348833", +"duckdb": "d41d40c68507f4d02e348833", +"generic": "d41d40c68507f4d02e348833", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d41d40c68507f4d02e348833", +"postgres": "d41d40c68507f4d02e348833", +"postgresql": "d41d40c68507f4d02e348833", +"snowflake": "d41d40c68507f4d02e348833", +"tsql": "d41d40c68507f4d02e348833" +} +], +[ +"type", +{ +"DuckDB": "2c44dc35fd1ef8701101802e", +"None": "2c44dc35fd1ef8701101802e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "2c44dc35fd1ef8701101802e", +"duckdb": "2c44dc35fd1ef8701101802e", +"generic": "2c44dc35fd1ef8701101802e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c44dc35fd1ef8701101802e", +"postgres": "2c44dc35fd1ef8701101802e", +"postgresql": "2c44dc35fd1ef8701101802e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "2c44dc35fd1ef8701101802e" +} +], +[ +"type", +{ +"DuckDB": "599b3293ed620803ce2401b5", +"None": "599b3293ed620803ce2401b5", +"bigquery": "599b3293ed620803ce2401b5", +"databricks": "599b3293ed620803ce2401b5", +"duckdb": "599b3293ed620803ce2401b5", +"generic": "599b3293ed620803ce2401b5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "599b3293ed620803ce2401b5", +"postgres": "599b3293ed620803ce2401b5", +"postgresql": "599b3293ed620803ce2401b5", +"snowflake": "599b3293ed620803ce2401b5", +"tsql": "599b3293ed620803ce2401b5" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "9e4a50a8aab648a9737f7d0c", +"None": "9e4a50a8aab648a9737f7d0c", +"bigquery": "d9a4e245156e760303573500", +"databricks": "9e4a50a8aab648a9737f7d0c", +"duckdb": "9e4a50a8aab648a9737f7d0c", +"generic": "9e4a50a8aab648a9737f7d0c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9e4a50a8aab648a9737f7d0c", +"postgres": "36d29258901a4d8c296baa68", +"postgresql": "36d29258901a4d8c296baa68", +"snowflake": "9e4a50a8aab648a9737f7d0c", +"tsql": "355e032cc5c0f24695836b3d" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "9c4e957c3856f1159283313a", +"None": "8731ee5ed03816bf6b4a8464", +"bigquery": "657a69dabb6a9b2c7b834d41", +"databricks": "606d813c3f1dfcf4921045c8", +"duckdb": "9c4e957c3856f1159283313a", +"generic": "8731ee5ed03816bf6b4a8464", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b9f1a78bcda3bc968346cc10", +"postgres": "9c4e957c3856f1159283313a", +"postgresql": "9c4e957c3856f1159283313a", +"snowflake": "cd2eb5ae8b54388fde5d5737", +"tsql": "8619573249fac6f54d0bc771" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "9ebaaf01ad47894d796ac621", +"None": "00caaa5ad0660c8df2165071", +"bigquery": "18cb3eafdbb91b315055426b", +"databricks": "7321b96df327a001959868b6", +"duckdb": "9ebaaf01ad47894d796ac621", +"generic": "00caaa5ad0660c8df2165071", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "00caaa5ad0660c8df2165071", +"postgres": "00caaa5ad0660c8df2165071", +"postgresql": "00caaa5ad0660c8df2165071", +"snowflake": "d5bfcc48faca639b47d37309", +"tsql": "00caaa5ad0660c8df2165071" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "7a79790250ede37cbc407ce6", +"None": "4425df9f96e51bc0984eab91", +"bigquery": "135a912ea74f76a35d9d0632", +"databricks": "4425df9f96e51bc0984eab91", +"duckdb": "7a79790250ede37cbc407ce6", +"generic": "4425df9f96e51bc0984eab91", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "7a79790250ede37cbc407ce6", +"postgresql": "7a79790250ede37cbc407ce6", +"snowflake": "061ecb71b8d0a25345bb4dc5", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "63c80b5d8e861216f0c45bfd", +"None": "d227b99a82e505d599c2ed8a", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "d227b99a82e505d599c2ed8a", +"duckdb": "63c80b5d8e861216f0c45bfd", +"generic": "d227b99a82e505d599c2ed8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d227b99a82e505d599c2ed8a", +"postgres": "d227b99a82e505d599c2ed8a", +"postgresql": "d227b99a82e505d599c2ed8a", +"snowflake": "d227b99a82e505d599c2ed8a", +"tsql": "d227b99a82e505d599c2ed8a" +} +], +[ +"type", +{ +"DuckDB": "4ed568ea1adb2c8a7141be20", +"None": "b981394a3f4d961a44e73868", +"bigquery": "b981394a3f4d961a44e73868", +"databricks": "b981394a3f4d961a44e73868", +"duckdb": "4ed568ea1adb2c8a7141be20", +"generic": "b981394a3f4d961a44e73868", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "4ed568ea1adb2c8a7141be20", +"postgresql": "4ed568ea1adb2c8a7141be20", +"snowflake": "b63ef04215d2dd65ccf32905", +"tsql": "c963e03af3ddb9731cbb40e3" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "f46cf4b4e131aa122b5b33d6", +"None": "f46cf4b4e131aa122b5b33d6", +"bigquery": "f46cf4b4e131aa122b5b33d6", +"databricks": "f46cf4b4e131aa122b5b33d6", +"duckdb": "f46cf4b4e131aa122b5b33d6", +"generic": "f46cf4b4e131aa122b5b33d6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f46cf4b4e131aa122b5b33d6", +"postgres": "f46cf4b4e131aa122b5b33d6", +"postgresql": "f46cf4b4e131aa122b5b33d6", +"snowflake": "f46cf4b4e131aa122b5b33d6", +"tsql": "f46cf4b4e131aa122b5b33d6" +} +], +[ +"type", +{ +"DuckDB": "0da0fad3107f7a26744c9411", +"None": "4172129e8d84a0da597bc612", +"bigquery": "d138b3b3355094a33a14355a", +"databricks": "4172129e8d84a0da597bc612", +"duckdb": "0da0fad3107f7a26744c9411", +"generic": "4172129e8d84a0da597bc612", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "0da0fad3107f7a26744c9411", +"postgresql": "0da0fad3107f7a26744c9411", +"snowflake": "e21aafb74f6e5c5e181abd5e", +"tsql": "2e261f2f5a01e0e0f4a34a7a" +} +], +[ +"type", +{ +"DuckDB": "5c528c7b4329aeb692ec6e94", +"None": "5c528c7b4329aeb692ec6e94", +"bigquery": "5c528c7b4329aeb692ec6e94", +"databricks": "5c528c7b4329aeb692ec6e94", +"duckdb": "5c528c7b4329aeb692ec6e94", +"generic": "5c528c7b4329aeb692ec6e94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c528c7b4329aeb692ec6e94", +"postgres": "5c528c7b4329aeb692ec6e94", +"postgresql": "5c528c7b4329aeb692ec6e94", +"snowflake": "5c528c7b4329aeb692ec6e94", +"tsql": "5c528c7b4329aeb692ec6e94" +} +], +[ +"type", +{ +"DuckDB": "b66ef401bfc62ea5f67697f0", +"None": "b66ef401bfc62ea5f67697f0", +"bigquery": "b66ef401bfc62ea5f67697f0", +"databricks": "b66ef401bfc62ea5f67697f0", +"duckdb": "b66ef401bfc62ea5f67697f0", +"generic": "b66ef401bfc62ea5f67697f0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b66ef401bfc62ea5f67697f0", +"postgres": "b66ef401bfc62ea5f67697f0", +"postgresql": "b66ef401bfc62ea5f67697f0", +"snowflake": "b66ef401bfc62ea5f67697f0", +"tsql": "b66ef401bfc62ea5f67697f0" +} +], +[ +"type", +{ +"DuckDB": "67b955e24db58c73ccf37441", +"None": "7390a6531648a24d91197039", +"bigquery": "41beda08d4df864cb9dd8402", +"databricks": "41beda08d4df864cb9dd8402", +"duckdb": "67b955e24db58c73ccf37441", +"generic": "7390a6531648a24d91197039", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "41beda08d4df864cb9dd8402", +"postgres": "4d6c7e32aac20e9e4fb2369e", +"postgresql": "4d6c7e32aac20e9e4fb2369e", +"snowflake": "bae18d740565e7e0e416c8f3", +"tsql": "762b596d819e7050cff38fb8" +} +], +[ +"type", +{ +"DuckDB": "c4346d0257336219f1d26890", +"None": "c4346d0257336219f1d26890", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "c4346d0257336219f1d26890", +"duckdb": "c4346d0257336219f1d26890", +"generic": "c4346d0257336219f1d26890", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c4346d0257336219f1d26890", +"postgres": "c4346d0257336219f1d26890", +"postgresql": "c4346d0257336219f1d26890", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "c4346d0257336219f1d26890" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "f6c3d80bfee0ec0b286b82b5", +"None": "b137c998bdaa2555d912e8fa", +"bigquery": "b137c998bdaa2555d912e8fa", +"databricks": "b137c998bdaa2555d912e8fa", +"duckdb": "f6c3d80bfee0ec0b286b82b5", +"generic": "b137c998bdaa2555d912e8fa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b137c998bdaa2555d912e8fa", +"postgres": "b137c998bdaa2555d912e8fa", +"postgresql": "b137c998bdaa2555d912e8fa", +"snowflake": "b137c998bdaa2555d912e8fa", +"tsql": "b137c998bdaa2555d912e8fa" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "05ebf79d18b2ae4eaa41f450", +"None": "336fd1b7a1f7d8105c29280c", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "336fd1b7a1f7d8105c29280c", +"duckdb": "05ebf79d18b2ae4eaa41f450", +"generic": "336fd1b7a1f7d8105c29280c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "336fd1b7a1f7d8105c29280c", +"postgres": "336fd1b7a1f7d8105c29280c", +"postgresql": "336fd1b7a1f7d8105c29280c", +"snowflake": "336fd1b7a1f7d8105c29280c", +"tsql": "336fd1b7a1f7d8105c29280c" +} +], +[ +"type", +{ +"DuckDB": "0960c90223e1eca15361360f", +"None": "0960c90223e1eca15361360f", +"bigquery": "0960c90223e1eca15361360f", +"databricks": "0960c90223e1eca15361360f", +"duckdb": "0960c90223e1eca15361360f", +"generic": "0960c90223e1eca15361360f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0960c90223e1eca15361360f", +"postgres": "0960c90223e1eca15361360f", +"postgresql": "0960c90223e1eca15361360f", +"snowflake": "0960c90223e1eca15361360f", +"tsql": "0960c90223e1eca15361360f" +} +], +[ +"type", +{ +"DuckDB": "b46dfa976d5ba04f95bea32b", +"None": "b46dfa976d5ba04f95bea32b", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "b46dfa976d5ba04f95bea32b", +"duckdb": "b46dfa976d5ba04f95bea32b", +"generic": "b46dfa976d5ba04f95bea32b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b46dfa976d5ba04f95bea32b", +"postgres": "34dcdfe3e3afe6140e1353e0", +"postgresql": "34dcdfe3e3afe6140e1353e0", +"snowflake": "b46dfa976d5ba04f95bea32b", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "b66ef401bfc62ea5f67697f0", +"None": "b66ef401bfc62ea5f67697f0", +"bigquery": "b66ef401bfc62ea5f67697f0", +"databricks": "b66ef401bfc62ea5f67697f0", +"duckdb": "b66ef401bfc62ea5f67697f0", +"generic": "b66ef401bfc62ea5f67697f0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b66ef401bfc62ea5f67697f0", +"postgres": "b66ef401bfc62ea5f67697f0", +"postgresql": "b66ef401bfc62ea5f67697f0", +"snowflake": "b66ef401bfc62ea5f67697f0", +"tsql": "b66ef401bfc62ea5f67697f0" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "66f7ca02161349d637fa7d4e", +"None": "66f7ca02161349d637fa7d4e", +"bigquery": "d90a78129f7050a540984f6f", +"databricks": "d90a78129f7050a540984f6f", +"duckdb": "66f7ca02161349d637fa7d4e", +"generic": "66f7ca02161349d637fa7d4e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d90a78129f7050a540984f6f", +"postgres": "66f7ca02161349d637fa7d4e", +"postgresql": "66f7ca02161349d637fa7d4e", +"snowflake": "66f7ca02161349d637fa7d4e", +"tsql": "66f7ca02161349d637fa7d4e" +} +], +[ +"type", +{ +"DuckDB": "6b7315cdb8da9cc8682fe582", +"None": "5db36cde08c21101926533c6", +"bigquery": "be1298bd832c06e2fbed16cc", +"databricks": "68c9a65b028d0f4e1e431d2b", +"duckdb": "6b7315cdb8da9cc8682fe582", +"generic": "5db36cde08c21101926533c6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3a28322a1d9fe5f73ddf21a6", +"postgres": "627a2125b73642795b93f3ec", +"postgresql": "627a2125b73642795b93f3ec", +"snowflake": "c990ce1e11ec35da455f1547", +"tsql": "5db36cde08c21101926533c6" +} +], +[ +"type", +{ +"DuckDB": "26e466cea9d3c66c226e9bbb", +"None": "26e466cea9d3c66c226e9bbb", +"bigquery": "26e466cea9d3c66c226e9bbb", +"databricks": "26e466cea9d3c66c226e9bbb", +"duckdb": "26e466cea9d3c66c226e9bbb", +"generic": "26e466cea9d3c66c226e9bbb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "26e466cea9d3c66c226e9bbb", +"postgres": "26e466cea9d3c66c226e9bbb", +"postgresql": "26e466cea9d3c66c226e9bbb", +"snowflake": "26e466cea9d3c66c226e9bbb", +"tsql": "26e466cea9d3c66c226e9bbb" +} +], +[ +"type", +{ +"DuckDB": "31efc7b307bffec917380e8e", +"None": "31efc7b307bffec917380e8e", +"bigquery": "31efc7b307bffec917380e8e", +"databricks": "31efc7b307bffec917380e8e", +"duckdb": "31efc7b307bffec917380e8e", +"generic": "31efc7b307bffec917380e8e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "31efc7b307bffec917380e8e", +"postgres": "31efc7b307bffec917380e8e", +"postgresql": "31efc7b307bffec917380e8e", +"snowflake": "42d4a23d9a52e79d0c3458d3", +"tsql": "31efc7b307bffec917380e8e" +} +], +[ +"type", +{ +"DuckDB": "2f9ac8bd6831b78665301f10", +"None": "2f9ac8bd6831b78665301f10", +"bigquery": "2f9ac8bd6831b78665301f10", +"databricks": "2f9ac8bd6831b78665301f10", +"duckdb": "2f9ac8bd6831b78665301f10", +"generic": "2f9ac8bd6831b78665301f10", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2f9ac8bd6831b78665301f10", +"postgres": "2f9ac8bd6831b78665301f10", +"postgresql": "2f9ac8bd6831b78665301f10", +"snowflake": "2f9ac8bd6831b78665301f10", +"tsql": "2f9ac8bd6831b78665301f10" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "7102b9514dfef58f8673a6bf", +"None": "7102b9514dfef58f8673a6bf", +"bigquery": "7102b9514dfef58f8673a6bf", +"databricks": "7102b9514dfef58f8673a6bf", +"duckdb": "7102b9514dfef58f8673a6bf", +"generic": "7102b9514dfef58f8673a6bf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7102b9514dfef58f8673a6bf", +"postgres": "7102b9514dfef58f8673a6bf", +"postgresql": "7102b9514dfef58f8673a6bf", +"snowflake": "7102b9514dfef58f8673a6bf", +"tsql": "7102b9514dfef58f8673a6bf" +} +], +[ +"type", +{ +"DuckDB": "6d7ab232e65fe8d85c7169ae", +"None": "6d7ab232e65fe8d85c7169ae", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "6d7ab232e65fe8d85c7169ae", +"duckdb": "6d7ab232e65fe8d85c7169ae", +"generic": "6d7ab232e65fe8d85c7169ae", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6d7ab232e65fe8d85c7169ae", +"postgres": "a6659d96bbfa5da75e69e22d", +"postgresql": "a6659d96bbfa5da75e69e22d", +"snowflake": "6d7ab232e65fe8d85c7169ae", +"tsql": "aed0419e7f2ce7c0a8892248" +} +], +[ +"type", +{ +"DuckDB": "e7683084f9cefaf3748cf526", +"None": "e7683084f9cefaf3748cf526", +"bigquery": "e7683084f9cefaf3748cf526", +"databricks": "e7683084f9cefaf3748cf526", +"duckdb": "e7683084f9cefaf3748cf526", +"generic": "e7683084f9cefaf3748cf526", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7683084f9cefaf3748cf526", +"postgres": "e7683084f9cefaf3748cf526", +"postgresql": "e7683084f9cefaf3748cf526", +"snowflake": "e7683084f9cefaf3748cf526", +"tsql": "e7683084f9cefaf3748cf526" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "50a940812c6ed2e6dd842e74", +"None": "50a940812c6ed2e6dd842e74", +"bigquery": "50a940812c6ed2e6dd842e74", +"databricks": "50a940812c6ed2e6dd842e74", +"duckdb": "50a940812c6ed2e6dd842e74", +"generic": "50a940812c6ed2e6dd842e74", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "50a940812c6ed2e6dd842e74", +"postgres": "50a940812c6ed2e6dd842e74", +"postgresql": "50a940812c6ed2e6dd842e74", +"snowflake": "50a940812c6ed2e6dd842e74", +"tsql": "50a940812c6ed2e6dd842e74" +} +], +[ +"type", +{ +"DuckDB": "e1c74d96ac578f9323bf1bbd", +"None": "e1c74d96ac578f9323bf1bbd", +"bigquery": "e1c74d96ac578f9323bf1bbd", +"databricks": "e1c74d96ac578f9323bf1bbd", +"duckdb": "e1c74d96ac578f9323bf1bbd", +"generic": "e1c74d96ac578f9323bf1bbd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c74d96ac578f9323bf1bbd", +"postgres": "e1c74d96ac578f9323bf1bbd", +"postgresql": "e1c74d96ac578f9323bf1bbd", +"snowflake": "e1c74d96ac578f9323bf1bbd", +"tsql": "e1c74d96ac578f9323bf1bbd" +} +], +[ +"type", +{ +"DuckDB": "d6e37cd37419bfb26e09b080", +"None": "d6e37cd37419bfb26e09b080", +"bigquery": "d6e37cd37419bfb26e09b080", +"databricks": "d6e37cd37419bfb26e09b080", +"duckdb": "d6e37cd37419bfb26e09b080", +"generic": "d6e37cd37419bfb26e09b080", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6e37cd37419bfb26e09b080", +"postgres": "d6e37cd37419bfb26e09b080", +"postgresql": "d6e37cd37419bfb26e09b080", +"snowflake": "d6e37cd37419bfb26e09b080", +"tsql": "d6e37cd37419bfb26e09b080" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "7a79790250ede37cbc407ce6", +"None": "4425df9f96e51bc0984eab91", +"bigquery": "135a912ea74f76a35d9d0632", +"databricks": "4425df9f96e51bc0984eab91", +"duckdb": "7a79790250ede37cbc407ce6", +"generic": "4425df9f96e51bc0984eab91", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "7a79790250ede37cbc407ce6", +"postgresql": "7a79790250ede37cbc407ce6", +"snowflake": "061ecb71b8d0a25345bb4dc5", +"tsql": "45809055304b61252670382f" +} +], +[ +"type", +{ +"DuckDB": "c7b697bbfa82dd2821ad64e9", +"None": "c7b697bbfa82dd2821ad64e9", +"bigquery": "dcb00b7053ffe69fe3ce99cd", +"databricks": "dcb00b7053ffe69fe3ce99cd", +"duckdb": "c7b697bbfa82dd2821ad64e9", +"generic": "c7b697bbfa82dd2821ad64e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dcb00b7053ffe69fe3ce99cd", +"postgres": "c7b697bbfa82dd2821ad64e9", +"postgresql": "c7b697bbfa82dd2821ad64e9", +"snowflake": "c7b697bbfa82dd2821ad64e9", +"tsql": "c7b697bbfa82dd2821ad64e9" +} +], +[ +"type", +{ +"DuckDB": "66acfa34471e60551c71ca8a", +"None": "66acfa34471e60551c71ca8a", +"bigquery": "66acfa34471e60551c71ca8a", +"databricks": "66acfa34471e60551c71ca8a", +"duckdb": "66acfa34471e60551c71ca8a", +"generic": "66acfa34471e60551c71ca8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "66acfa34471e60551c71ca8a", +"postgres": "66acfa34471e60551c71ca8a", +"postgresql": "66acfa34471e60551c71ca8a", +"snowflake": "6306b9e88f7b13dbb6a32c00", +"tsql": "66acfa34471e60551c71ca8a" +} +], +[ +"type", +{ +"DuckDB": "793afe64018cd40f2abf8cd7", +"None": "793afe64018cd40f2abf8cd7", +"bigquery": "293fbfa51e5b07f0e2aee3d3", +"databricks": "bca4e355ae48af6503cb15b3", +"duckdb": "793afe64018cd40f2abf8cd7", +"generic": "793afe64018cd40f2abf8cd7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "793afe64018cd40f2abf8cd7", +"postgres": "793afe64018cd40f2abf8cd7", +"postgresql": "793afe64018cd40f2abf8cd7", +"snowflake": "c0351067a884fbee21c680b3", +"tsql": "793afe64018cd40f2abf8cd7" +} +], +[ +"type", +{ +"DuckDB": "0c40d2d16c130d68a7c6c41d", +"None": "0c40d2d16c130d68a7c6c41d", +"bigquery": "0c40d2d16c130d68a7c6c41d", +"databricks": "0c40d2d16c130d68a7c6c41d", +"duckdb": "0c40d2d16c130d68a7c6c41d", +"generic": "0c40d2d16c130d68a7c6c41d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c40d2d16c130d68a7c6c41d", +"postgres": "0c40d2d16c130d68a7c6c41d", +"postgresql": "0c40d2d16c130d68a7c6c41d", +"snowflake": "0c40d2d16c130d68a7c6c41d", +"tsql": "0c40d2d16c130d68a7c6c41d" +} +], +[ +"type", +{ +"DuckDB": "e1c36a5cdd2427ff2b089ac9", +"None": "e1c36a5cdd2427ff2b089ac9", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "e1c36a5cdd2427ff2b089ac9", +"duckdb": "e1c36a5cdd2427ff2b089ac9", +"generic": "e1c36a5cdd2427ff2b089ac9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c36a5cdd2427ff2b089ac9", +"postgres": "e1c36a5cdd2427ff2b089ac9", +"postgresql": "e1c36a5cdd2427ff2b089ac9", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "e1c36a5cdd2427ff2b089ac9" +} +], +[ +"type", +{ +"DuckDB": "f73576af0cd50cbe498a0092", +"None": "f73576af0cd50cbe498a0092", +"bigquery": "f73576af0cd50cbe498a0092", +"databricks": "f73576af0cd50cbe498a0092", +"duckdb": "f73576af0cd50cbe498a0092", +"generic": "f73576af0cd50cbe498a0092", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f73576af0cd50cbe498a0092", +"postgres": "f73576af0cd50cbe498a0092", +"postgresql": "f73576af0cd50cbe498a0092", +"snowflake": "f73576af0cd50cbe498a0092", +"tsql": "f73576af0cd50cbe498a0092" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "83fdbd1f0a6ee4e72faa4d94", +"None": "83fdbd1f0a6ee4e72faa4d94", +"bigquery": "83fdbd1f0a6ee4e72faa4d94", +"databricks": "83fdbd1f0a6ee4e72faa4d94", +"duckdb": "83fdbd1f0a6ee4e72faa4d94", +"generic": "83fdbd1f0a6ee4e72faa4d94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "83fdbd1f0a6ee4e72faa4d94", +"postgres": "83fdbd1f0a6ee4e72faa4d94", +"postgresql": "83fdbd1f0a6ee4e72faa4d94", +"snowflake": "83fdbd1f0a6ee4e72faa4d94", +"tsql": "83fdbd1f0a6ee4e72faa4d94" +} +], +[ +"type", +{ +"DuckDB": "355e032cc5c0f24695836b3d", +"None": "355e032cc5c0f24695836b3d", +"bigquery": "d9a4e245156e760303573500", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "355e032cc5c0f24695836b3d", +"generic": "355e032cc5c0f24695836b3d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "355e032cc5c0f24695836b3d", +"postgres": "b0b09c4a6f7570ece56f59ce", +"postgresql": "b0b09c4a6f7570ece56f59ce", +"snowflake": "355e032cc5c0f24695836b3d", +"tsql": "355e032cc5c0f24695836b3d" +} +], +[ +"type", +{ +"DuckDB": "a40042061a75dff39acf27ea", +"None": "a40042061a75dff39acf27ea", +"bigquery": "a40042061a75dff39acf27ea", +"databricks": "a40042061a75dff39acf27ea", +"duckdb": "a40042061a75dff39acf27ea", +"generic": "a40042061a75dff39acf27ea", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a40042061a75dff39acf27ea", +"postgres": "a40042061a75dff39acf27ea", +"postgresql": "a40042061a75dff39acf27ea", +"snowflake": "a40042061a75dff39acf27ea", +"tsql": "a40042061a75dff39acf27ea" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "d0c95c7a10940244afe0eb6c", +"None": "d0c95c7a10940244afe0eb6c", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "d0c95c7a10940244afe0eb6c", +"duckdb": "d0c95c7a10940244afe0eb6c", +"generic": "d0c95c7a10940244afe0eb6c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d0c95c7a10940244afe0eb6c", +"postgres": "d0c95c7a10940244afe0eb6c", +"postgresql": "d0c95c7a10940244afe0eb6c", +"snowflake": "d0c95c7a10940244afe0eb6c", +"tsql": "d0c95c7a10940244afe0eb6c" +} +], +[ +"type", +{ +"DuckDB": "5c28019dce48a74708c90a76", +"None": "5c28019dce48a74708c90a76", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "5c28019dce48a74708c90a76", +"duckdb": "5c28019dce48a74708c90a76", +"generic": "5c28019dce48a74708c90a76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "5c28019dce48a74708c90a76", +"postgresql": "5c28019dce48a74708c90a76", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "2a7a7132adc795ca2bc39c37" +} +], +[ +"type", +{ +"DuckDB": "2e4ac90e0dc84f220169a428", +"None": "2e4ac90e0dc84f220169a428", +"bigquery": "2e4ac90e0dc84f220169a428", +"databricks": "2e4ac90e0dc84f220169a428", +"duckdb": "2e4ac90e0dc84f220169a428", +"generic": "2e4ac90e0dc84f220169a428", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2e4ac90e0dc84f220169a428", +"postgres": "2e4ac90e0dc84f220169a428", +"postgresql": "2e4ac90e0dc84f220169a428", +"snowflake": "2e4ac90e0dc84f220169a428", +"tsql": "2e4ac90e0dc84f220169a428" +} +], +[ +"type", +{ +"DuckDB": "f94b458b28b4e92e314740bf", +"None": "f94b458b28b4e92e314740bf", +"bigquery": "f94b458b28b4e92e314740bf", +"databricks": "f94b458b28b4e92e314740bf", +"duckdb": "f94b458b28b4e92e314740bf", +"generic": "f94b458b28b4e92e314740bf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f94b458b28b4e92e314740bf", +"postgres": "f94b458b28b4e92e314740bf", +"postgresql": "f94b458b28b4e92e314740bf", +"snowflake": "f94b458b28b4e92e314740bf", +"tsql": "f94b458b28b4e92e314740bf" +} +], +[ +"type", +{ +"DuckDB": "2705f19942a4194a72576911", +"None": "2705f19942a4194a72576911", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "2705f19942a4194a72576911", +"duckdb": "2705f19942a4194a72576911", +"generic": "2705f19942a4194a72576911", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2705f19942a4194a72576911", +"postgres": "2705f19942a4194a72576911", +"postgresql": "2705f19942a4194a72576911", +"snowflake": "2705f19942a4194a72576911", +"tsql": "2705f19942a4194a72576911" +} +], +[ +"type", +{ +"DuckDB": "42a76d5ff05d0f91911ff368", +"None": "42a76d5ff05d0f91911ff368", +"bigquery": "42a76d5ff05d0f91911ff368", +"databricks": "42a76d5ff05d0f91911ff368", +"duckdb": "42a76d5ff05d0f91911ff368", +"generic": "42a76d5ff05d0f91911ff368", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42a76d5ff05d0f91911ff368", +"postgres": "42a76d5ff05d0f91911ff368", +"postgresql": "42a76d5ff05d0f91911ff368", +"snowflake": "42a76d5ff05d0f91911ff368", +"tsql": "42a76d5ff05d0f91911ff368" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "f94b458b28b4e92e314740bf", +"None": "f94b458b28b4e92e314740bf", +"bigquery": "f94b458b28b4e92e314740bf", +"databricks": "f94b458b28b4e92e314740bf", +"duckdb": "f94b458b28b4e92e314740bf", +"generic": "f94b458b28b4e92e314740bf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f94b458b28b4e92e314740bf", +"postgres": "f94b458b28b4e92e314740bf", +"postgresql": "f94b458b28b4e92e314740bf", +"snowflake": "f94b458b28b4e92e314740bf", +"tsql": "f94b458b28b4e92e314740bf" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "7c11e0be7630905d93bd9ce1", +"None": "7c11e0be7630905d93bd9ce1", +"bigquery": "7c11e0be7630905d93bd9ce1", +"databricks": "7c11e0be7630905d93bd9ce1", +"duckdb": "7c11e0be7630905d93bd9ce1", +"generic": "7c11e0be7630905d93bd9ce1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7c11e0be7630905d93bd9ce1", +"postgres": "7c11e0be7630905d93bd9ce1", +"postgresql": "7c11e0be7630905d93bd9ce1", +"snowflake": "7c11e0be7630905d93bd9ce1", +"tsql": "7c11e0be7630905d93bd9ce1" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "181c436b3522e70edf3adf58", +"None": "181c436b3522e70edf3adf58", +"bigquery": "181c436b3522e70edf3adf58", +"databricks": "181c436b3522e70edf3adf58", +"duckdb": "181c436b3522e70edf3adf58", +"generic": "181c436b3522e70edf3adf58", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "181c436b3522e70edf3adf58", +"postgres": "181c436b3522e70edf3adf58", +"postgresql": "181c436b3522e70edf3adf58", +"snowflake": "181c436b3522e70edf3adf58", +"tsql": "181c436b3522e70edf3adf58" +} +], +[ +"type", +{ +"DuckDB": "24c3d9dcce7ee2c5b2bac1df", +"None": "547c73a68a3263d4ca786f4d", +"bigquery": "547c73a68a3263d4ca786f4d", +"databricks": "3ad35a92f1fdd0560316b488", +"duckdb": "24c3d9dcce7ee2c5b2bac1df", +"generic": "547c73a68a3263d4ca786f4d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "24c3d9dcce7ee2c5b2bac1df", +"postgresql": "24c3d9dcce7ee2c5b2bac1df", +"snowflake": "0591bc1bf9f4f62b80103d53", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "b60a7259ca26ea325ba090a1", +"None": "b60a7259ca26ea325ba090a1", +"bigquery": "b60a7259ca26ea325ba090a1", +"databricks": "b60a7259ca26ea325ba090a1", +"duckdb": "b60a7259ca26ea325ba090a1", +"generic": "b60a7259ca26ea325ba090a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b60a7259ca26ea325ba090a1", +"postgres": "b60a7259ca26ea325ba090a1", +"postgresql": "b60a7259ca26ea325ba090a1", +"snowflake": "b60a7259ca26ea325ba090a1", +"tsql": "b60a7259ca26ea325ba090a1" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "285056becb5d467811be7cd6", +"None": "285056becb5d467811be7cd6", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "285056becb5d467811be7cd6", +"duckdb": "285056becb5d467811be7cd6", +"generic": "285056becb5d467811be7cd6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fc10fc558d906a80a6404b7f", +"postgres": "285056becb5d467811be7cd6", +"postgresql": "285056becb5d467811be7cd6", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "285056becb5d467811be7cd6" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "a3b6b25265be330ec52a3322", +"None": "a3b6b25265be330ec52a3322", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "a3b6b25265be330ec52a3322", +"generic": "a3b6b25265be330ec52a3322", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "a3b6b25265be330ec52a3322" +} +], +[ +"type", +{ +"DuckDB": "4d0f0db1a550ed1e0da1a043", +"None": "4d0f0db1a550ed1e0da1a043", +"bigquery": "4d0f0db1a550ed1e0da1a043", +"databricks": "4d0f0db1a550ed1e0da1a043", +"duckdb": "4d0f0db1a550ed1e0da1a043", +"generic": "4d0f0db1a550ed1e0da1a043", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4d0f0db1a550ed1e0da1a043", +"postgres": "4d0f0db1a550ed1e0da1a043", +"postgresql": "4d0f0db1a550ed1e0da1a043", +"snowflake": "4d0f0db1a550ed1e0da1a043", +"tsql": "4d0f0db1a550ed1e0da1a043" +} +], +[ +"type", +{ +"DuckDB": "6f4372a332076048e56d5076", +"None": "6f4372a332076048e56d5076", +"bigquery": "74a30f6aa9def0b58afc616c", +"databricks": "6f4372a332076048e56d5076", +"duckdb": "6f4372a332076048e56d5076", +"generic": "6f4372a332076048e56d5076", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6f4372a332076048e56d5076", +"postgres": "6f4372a332076048e56d5076", +"postgresql": "6f4372a332076048e56d5076", +"snowflake": "daeeb0f00b3ee4211b8da00f", +"tsql": "6f4372a332076048e56d5076" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "e8b6e0bd2ded7b9eec6209e4", +"None": "e8b6e0bd2ded7b9eec6209e4", +"bigquery": "e8b6e0bd2ded7b9eec6209e4", +"databricks": "e8b6e0bd2ded7b9eec6209e4", +"duckdb": "e8b6e0bd2ded7b9eec6209e4", +"generic": "e8b6e0bd2ded7b9eec6209e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8b6e0bd2ded7b9eec6209e4", +"postgres": "e8b6e0bd2ded7b9eec6209e4", +"postgresql": "e8b6e0bd2ded7b9eec6209e4", +"snowflake": "e8b6e0bd2ded7b9eec6209e4", +"tsql": "e8b6e0bd2ded7b9eec6209e4" +} +], +[ +"type", +{ +"DuckDB": "087a9417cb511ff7bcfd6ccb", +"None": "383f8922f4b6f167d384037d", +"bigquery": "383f8922f4b6f167d384037d", +"databricks": "383f8922f4b6f167d384037d", +"duckdb": "087a9417cb511ff7bcfd6ccb", +"generic": "383f8922f4b6f167d384037d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "383f8922f4b6f167d384037d", +"postgres": "383f8922f4b6f167d384037d", +"postgresql": "383f8922f4b6f167d384037d", +"snowflake": "383f8922f4b6f167d384037d", +"tsql": "383f8922f4b6f167d384037d" +} +], +[ +"type", +{ +"DuckDB": "e08a957777bd278f921ca0ee", +"None": "e08a957777bd278f921ca0ee", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "e08a957777bd278f921ca0ee", +"duckdb": "e08a957777bd278f921ca0ee", +"generic": "e08a957777bd278f921ca0ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e08a957777bd278f921ca0ee", +"postgres": "e08a957777bd278f921ca0ee", +"postgresql": "e08a957777bd278f921ca0ee", +"snowflake": "e08a957777bd278f921ca0ee", +"tsql": "e08a957777bd278f921ca0ee" +} +], +[ +"type", +{ +"DuckDB": "d79c3ac70093cb4cb531e71c", +"None": "d79c3ac70093cb4cb531e71c", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "d79c3ac70093cb4cb531e71c", +"duckdb": "d79c3ac70093cb4cb531e71c", +"generic": "d79c3ac70093cb4cb531e71c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d79c3ac70093cb4cb531e71c", +"postgres": "d79c3ac70093cb4cb531e71c", +"postgresql": "d79c3ac70093cb4cb531e71c", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d79c3ac70093cb4cb531e71c" +} +], +[ +"type", +{ +"DuckDB": "688179272da645629077e980", +"None": "688179272da645629077e980", +"bigquery": "688179272da645629077e980", +"databricks": "688179272da645629077e980", +"duckdb": "688179272da645629077e980", +"generic": "688179272da645629077e980", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "688179272da645629077e980", +"postgres": "688179272da645629077e980", +"postgresql": "688179272da645629077e980", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "688179272da645629077e980" +} +], +[ +"type", +{ +"DuckDB": "e08a957777bd278f921ca0ee", +"None": "e08a957777bd278f921ca0ee", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "e08a957777bd278f921ca0ee", +"duckdb": "e08a957777bd278f921ca0ee", +"generic": "e08a957777bd278f921ca0ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e08a957777bd278f921ca0ee", +"postgres": "e08a957777bd278f921ca0ee", +"postgresql": "e08a957777bd278f921ca0ee", +"snowflake": "e08a957777bd278f921ca0ee", +"tsql": "e08a957777bd278f921ca0ee" +} +], +[ +"type", +{ +"DuckDB": "3533f91995bca270f191503d", +"None": "3533f91995bca270f191503d", +"bigquery": "3533f91995bca270f191503d", +"databricks": "fcfd1f4cfe959c3d88cf43c4", +"duckdb": "3533f91995bca270f191503d", +"generic": "3533f91995bca270f191503d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "3533f91995bca270f191503d", +"postgresql": "3533f91995bca270f191503d", +"snowflake": "3533f91995bca270f191503d", +"tsql": "3533f91995bca270f191503d" +} +], +[ +"type", +{ +"DuckDB": "c2700ca3573352821e09af03", +"None": "c2700ca3573352821e09af03", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c2700ca3573352821e09af03", +"duckdb": "c2700ca3573352821e09af03", +"generic": "c2700ca3573352821e09af03", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c2700ca3573352821e09af03", +"postgres": "c2700ca3573352821e09af03", +"postgresql": "c2700ca3573352821e09af03", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e80839f7b11a9775b7596b50" +} +], +[ +"type", +{ +"DuckDB": "eea9a25a6fbf8e049a445513", +"None": "eea9a25a6fbf8e049a445513", +"bigquery": "eea9a25a6fbf8e049a445513", +"databricks": "eea9a25a6fbf8e049a445513", +"duckdb": "eea9a25a6fbf8e049a445513", +"generic": "eea9a25a6fbf8e049a445513", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eea9a25a6fbf8e049a445513", +"postgres": "eea9a25a6fbf8e049a445513", +"postgresql": "eea9a25a6fbf8e049a445513", +"snowflake": "eea9a25a6fbf8e049a445513", +"tsql": "eea9a25a6fbf8e049a445513" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "b7d6fc051a3c1ea6deb29ff4", +"postgresql": "b7d6fc051a3c1ea6deb29ff4", +"snowflake": "251aba3fef503ce64556c701", +"tsql": "e3bc1b8bad22d8104417b00f" +} +], +[ +"type", +{ +"DuckDB": "110327dc45eac0e8d54a96dc", +"None": "110327dc45eac0e8d54a96dc", +"bigquery": "1a804f317a3e9a0016925b04", +"databricks": "1a804f317a3e9a0016925b04", +"duckdb": "110327dc45eac0e8d54a96dc", +"generic": "110327dc45eac0e8d54a96dc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1a804f317a3e9a0016925b04", +"postgres": "110327dc45eac0e8d54a96dc", +"postgresql": "110327dc45eac0e8d54a96dc", +"snowflake": "110327dc45eac0e8d54a96dc", +"tsql": "110327dc45eac0e8d54a96dc" +} +], +[ +"type", +{ +"DuckDB": "d6e37cd37419bfb26e09b080", +"None": "d6e37cd37419bfb26e09b080", +"bigquery": "d6e37cd37419bfb26e09b080", +"databricks": "d6e37cd37419bfb26e09b080", +"duckdb": "d6e37cd37419bfb26e09b080", +"generic": "d6e37cd37419bfb26e09b080", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6e37cd37419bfb26e09b080", +"postgres": "d6e37cd37419bfb26e09b080", +"postgresql": "d6e37cd37419bfb26e09b080", +"snowflake": "d6e37cd37419bfb26e09b080", +"tsql": "d6e37cd37419bfb26e09b080" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "e060b5a5a8e9774b580d58d3", +"None": "e060b5a5a8e9774b580d58d3", +"bigquery": "319a9fbb02ed503eb114164b", +"databricks": "319a9fbb02ed503eb114164b", +"duckdb": "e060b5a5a8e9774b580d58d3", +"generic": "e060b5a5a8e9774b580d58d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "319a9fbb02ed503eb114164b", +"postgres": "e060b5a5a8e9774b580d58d3", +"postgresql": "e060b5a5a8e9774b580d58d3", +"snowflake": "e060b5a5a8e9774b580d58d3", +"tsql": "e060b5a5a8e9774b580d58d3" +} +], +[ +"type", +{ +"DuckDB": "c6654395c62d21b415299f72", +"None": "c6654395c62d21b415299f72", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "c6654395c62d21b415299f72", +"duckdb": "c6654395c62d21b415299f72", +"generic": "c6654395c62d21b415299f72", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2978a8fe925c7f93c6e9fcfb", +"postgres": "c6654395c62d21b415299f72", +"postgresql": "c6654395c62d21b415299f72", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "c6654395c62d21b415299f72" +} +], +[ +"type", +{ +"DuckDB": "b0fba0f56f0cfe71621226b5", +"None": "b0fba0f56f0cfe71621226b5", +"bigquery": "f7fffd42e662ab4655dceb3b", +"databricks": "94b22f27d5086eaa9bb3eff5", +"duckdb": "b0fba0f56f0cfe71621226b5", +"generic": "b0fba0f56f0cfe71621226b5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b0fba0f56f0cfe71621226b5", +"postgres": "b0fba0f56f0cfe71621226b5", +"postgresql": "b0fba0f56f0cfe71621226b5", +"snowflake": "ffdce71ffba2fc524a0e9de5", +"tsql": "b0fba0f56f0cfe71621226b5" +} +], +[ +"type", +{ +"DuckDB": "63c80b5d8e861216f0c45bfd", +"None": "d227b99a82e505d599c2ed8a", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "d227b99a82e505d599c2ed8a", +"duckdb": "63c80b5d8e861216f0c45bfd", +"generic": "d227b99a82e505d599c2ed8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d227b99a82e505d599c2ed8a", +"postgres": "d227b99a82e505d599c2ed8a", +"postgresql": "d227b99a82e505d599c2ed8a", +"snowflake": "d227b99a82e505d599c2ed8a", +"tsql": "d227b99a82e505d599c2ed8a" +} +], +[ +"type", +{ +"DuckDB": "c2700ca3573352821e09af03", +"None": "c2700ca3573352821e09af03", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c2700ca3573352821e09af03", +"duckdb": "c2700ca3573352821e09af03", +"generic": "c2700ca3573352821e09af03", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c2700ca3573352821e09af03", +"postgres": "c2700ca3573352821e09af03", +"postgresql": "c2700ca3573352821e09af03", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e80839f7b11a9775b7596b50" +} +], +[ +"type", +{ +"DuckDB": "3915f3f73328f0838497b2d5", +"None": "3915f3f73328f0838497b2d5", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "3915f3f73328f0838497b2d5", +"duckdb": "3915f3f73328f0838497b2d5", +"generic": "3915f3f73328f0838497b2d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3915f3f73328f0838497b2d5", +"postgres": "3915f3f73328f0838497b2d5", +"postgresql": "3915f3f73328f0838497b2d5", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3915f3f73328f0838497b2d5" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "b55b02912e30e27dcb40ad93", +"None": "b55b02912e30e27dcb40ad93", +"bigquery": "b55b02912e30e27dcb40ad93", +"databricks": "b55b02912e30e27dcb40ad93", +"duckdb": "b55b02912e30e27dcb40ad93", +"generic": "b55b02912e30e27dcb40ad93", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b55b02912e30e27dcb40ad93", +"postgres": "b55b02912e30e27dcb40ad93", +"postgresql": "b55b02912e30e27dcb40ad93", +"snowflake": "b55b02912e30e27dcb40ad93", +"tsql": "b55b02912e30e27dcb40ad93" +} +], +[ +"type", +{ +"DuckDB": "e8cd7a4caa15ec2b6f990a1f", +"None": "1757eadf86c2a8956b520868", +"bigquery": "3688fe25b8bdff7eb938e6f9", +"databricks": "3688fe25b8bdff7eb938e6f9", +"duckdb": "e8cd7a4caa15ec2b6f990a1f", +"generic": "1757eadf86c2a8956b520868", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3688fe25b8bdff7eb938e6f9", +"postgres": "e8cd7a4caa15ec2b6f990a1f", +"postgresql": "e8cd7a4caa15ec2b6f990a1f", +"snowflake": "fe491937bc4ec7b061f2be43", +"tsql": "3688fe25b8bdff7eb938e6f9" +} +], +[ +"type", +{ +"DuckDB": "c6106f30f23f392e17eadef4", +"None": "c6106f30f23f392e17eadef4", +"bigquery": "c6106f30f23f392e17eadef4", +"databricks": "c6106f30f23f392e17eadef4", +"duckdb": "c6106f30f23f392e17eadef4", +"generic": "c6106f30f23f392e17eadef4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6106f30f23f392e17eadef4", +"postgres": "c6106f30f23f392e17eadef4", +"postgresql": "c6106f30f23f392e17eadef4", +"snowflake": "c6106f30f23f392e17eadef4", +"tsql": "c6106f30f23f392e17eadef4" +} +], +[ +"type", +{ +"DuckDB": "eb9c0f675dd300eb82500b21", +"None": "eb9c0f675dd300eb82500b21", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "eb9c0f675dd300eb82500b21", +"duckdb": "eb9c0f675dd300eb82500b21", +"generic": "eb9c0f675dd300eb82500b21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eb9c0f675dd300eb82500b21", +"postgres": "eb9c0f675dd300eb82500b21", +"postgresql": "eb9c0f675dd300eb82500b21", +"snowflake": "4e24a80b20b9dee0cbbfac17", +"tsql": "eb9c0f675dd300eb82500b21" +} +], +[ +"type", +{ +"DuckDB": "3eb44072dafa3bef75c97b6a", +"None": "3eb44072dafa3bef75c97b6a", +"bigquery": "3eb44072dafa3bef75c97b6a", +"databricks": "3eb44072dafa3bef75c97b6a", +"duckdb": "3eb44072dafa3bef75c97b6a", +"generic": "3eb44072dafa3bef75c97b6a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3eb44072dafa3bef75c97b6a", +"postgres": "3eb44072dafa3bef75c97b6a", +"postgresql": "3eb44072dafa3bef75c97b6a", +"snowflake": "3eb44072dafa3bef75c97b6a", +"tsql": "3eb44072dafa3bef75c97b6a" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "d9315b336b73c3d8d4b6e9a3", +"None": "f2ed52f6bf46b4db9343551c", +"bigquery": "f2ed52f6bf46b4db9343551c", +"databricks": "f2ed52f6bf46b4db9343551c", +"duckdb": "d9315b336b73c3d8d4b6e9a3", +"generic": "f2ed52f6bf46b4db9343551c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f2ed52f6bf46b4db9343551c", +"postgres": "f2ed52f6bf46b4db9343551c", +"postgresql": "f2ed52f6bf46b4db9343551c", +"snowflake": "f2ed52f6bf46b4db9343551c", +"tsql": "f2ed52f6bf46b4db9343551c" +} +], +[ +"type", +{ +"DuckDB": "0960c90223e1eca15361360f", +"None": "0960c90223e1eca15361360f", +"bigquery": "0960c90223e1eca15361360f", +"databricks": "0960c90223e1eca15361360f", +"duckdb": "0960c90223e1eca15361360f", +"generic": "0960c90223e1eca15361360f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0960c90223e1eca15361360f", +"postgres": "0960c90223e1eca15361360f", +"postgresql": "0960c90223e1eca15361360f", +"snowflake": "0960c90223e1eca15361360f", +"tsql": "0960c90223e1eca15361360f" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "11d8d18c76077fddaf1b264f", +"None": "11d8d18c76077fddaf1b264f", +"bigquery": "11d8d18c76077fddaf1b264f", +"databricks": "11d8d18c76077fddaf1b264f", +"duckdb": "11d8d18c76077fddaf1b264f", +"generic": "11d8d18c76077fddaf1b264f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "11d8d18c76077fddaf1b264f", +"postgres": "11d8d18c76077fddaf1b264f", +"postgresql": "11d8d18c76077fddaf1b264f", +"snowflake": "11d8d18c76077fddaf1b264f", +"tsql": "11d8d18c76077fddaf1b264f" +} +], +[ +"type", +{ +"DuckDB": "0b9d9879e289ea9a6a3e3e70", +"None": "0b9d9879e289ea9a6a3e3e70", +"bigquery": "0b9d9879e289ea9a6a3e3e70", +"databricks": "0b9d9879e289ea9a6a3e3e70", +"duckdb": "0b9d9879e289ea9a6a3e3e70", +"generic": "0b9d9879e289ea9a6a3e3e70", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b9d9879e289ea9a6a3e3e70", +"postgres": "0b9d9879e289ea9a6a3e3e70", +"postgresql": "0b9d9879e289ea9a6a3e3e70", +"snowflake": "0b9d9879e289ea9a6a3e3e70", +"tsql": "0b9d9879e289ea9a6a3e3e70" +} +], +[ +"type", +{ +"DuckDB": "8e484ef176389314b9fd318e", +"None": "8e484ef176389314b9fd318e", +"bigquery": "8e484ef176389314b9fd318e", +"databricks": "8e484ef176389314b9fd318e", +"duckdb": "8e484ef176389314b9fd318e", +"generic": "8e484ef176389314b9fd318e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8e484ef176389314b9fd318e", +"postgres": "8e484ef176389314b9fd318e", +"postgresql": "8e484ef176389314b9fd318e", +"snowflake": "8e484ef176389314b9fd318e", +"tsql": "8e484ef176389314b9fd318e" +} +], +[ +"type", +{ +"DuckDB": "41a317d0625259b54aa1dc94", +"None": "41a317d0625259b54aa1dc94", +"bigquery": "41a317d0625259b54aa1dc94", +"databricks": "41a317d0625259b54aa1dc94", +"duckdb": "41a317d0625259b54aa1dc94", +"generic": "41a317d0625259b54aa1dc94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "41a317d0625259b54aa1dc94", +"postgres": "41a317d0625259b54aa1dc94", +"postgresql": "41a317d0625259b54aa1dc94", +"snowflake": "41a317d0625259b54aa1dc94", +"tsql": "41a317d0625259b54aa1dc94" +} +], +[ +"type", +{ +"DuckDB": "f01435c3d6e460ffd86479dc", +"None": "f01435c3d6e460ffd86479dc", +"bigquery": "99d6d9358db2ff349db17bb6", +"databricks": "72d1587b51c517b53f52a2d8", +"duckdb": "f01435c3d6e460ffd86479dc", +"generic": "f01435c3d6e460ffd86479dc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f01435c3d6e460ffd86479dc", +"postgres": "f01435c3d6e460ffd86479dc", +"postgresql": "f01435c3d6e460ffd86479dc", +"snowflake": "143dfed5733eda8a7f6ed075", +"tsql": "f01435c3d6e460ffd86479dc" +} +], +[ +"type", +{ +"DuckDB": "23ee196b7bd6039855a1460f", +"None": "23ee196b7bd6039855a1460f", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "23ee196b7bd6039855a1460f", +"duckdb": "23ee196b7bd6039855a1460f", +"generic": "23ee196b7bd6039855a1460f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23ee196b7bd6039855a1460f", +"postgres": "23ee196b7bd6039855a1460f", +"postgresql": "23ee196b7bd6039855a1460f", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "23ee196b7bd6039855a1460f" +} +], +[ +"type", +{ +"DuckDB": "3e2cff9b165a6eaf1c043756", +"None": "77fe8e560c0d80d58e64a3ac", +"bigquery": "77fe8e560c0d80d58e64a3ac", +"databricks": "a8b5c14299c94dad25520df6", +"duckdb": "3e2cff9b165a6eaf1c043756", +"generic": "77fe8e560c0d80d58e64a3ac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "545b06c80ff7b812dafe5136", +"postgresql": "545b06c80ff7b812dafe5136", +"snowflake": "2a7a3265ae4026b435644d0e", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "29739921db4d47abc1ac18c7", +"None": "29739921db4d47abc1ac18c7", +"bigquery": "01d1b160c1768fa1cd4b2e45", +"databricks": "adbb06204fd66d8071c3dbc6", +"duckdb": "29739921db4d47abc1ac18c7", +"generic": "29739921db4d47abc1ac18c7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "29739921db4d47abc1ac18c7", +"postgres": "29739921db4d47abc1ac18c7", +"postgresql": "29739921db4d47abc1ac18c7", +"snowflake": "216a7f0ead009a087eea4e1c", +"tsql": "561143bc26e834af800f173b" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "f15c56475abfc73488abe63b", +"None": "f15c56475abfc73488abe63b", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f15c56475abfc73488abe63b", +"duckdb": "f15c56475abfc73488abe63b", +"generic": "f15c56475abfc73488abe63b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f15c56475abfc73488abe63b", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f15c56475abfc73488abe63b" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "53ea41ce10b9fa682ddf305d", +"None": "a1176e5228f8200c23e7a0a7", +"bigquery": "2795066c9a71fa3e3780c73b", +"databricks": "61058ba40f23266ededb040f", +"duckdb": "53ea41ce10b9fa682ddf305d", +"generic": "a1176e5228f8200c23e7a0a7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "a09c2bcd944462f73dd87c2d", +"postgresql": "a09c2bcd944462f73dd87c2d", +"snowflake": "9553636ef19ebb7706dabb5f", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "66078c43faec49916ed11546", +"None": "66078c43faec49916ed11546", +"bigquery": "66078c43faec49916ed11546", +"databricks": "66078c43faec49916ed11546", +"duckdb": "66078c43faec49916ed11546", +"generic": "66078c43faec49916ed11546", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "66078c43faec49916ed11546", +"postgres": "66078c43faec49916ed11546", +"postgresql": "66078c43faec49916ed11546", +"snowflake": "66078c43faec49916ed11546", +"tsql": "66078c43faec49916ed11546" +} +], +[ +"type", +{ +"DuckDB": "cea3ac691106cc77e45d9414", +"None": "cea3ac691106cc77e45d9414", +"bigquery": "cea3ac691106cc77e45d9414", +"databricks": "cea3ac691106cc77e45d9414", +"duckdb": "cea3ac691106cc77e45d9414", +"generic": "cea3ac691106cc77e45d9414", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cea3ac691106cc77e45d9414", +"postgres": "cea3ac691106cc77e45d9414", +"postgresql": "cea3ac691106cc77e45d9414", +"snowflake": "cea3ac691106cc77e45d9414", +"tsql": "cea3ac691106cc77e45d9414" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "937a574f550c49d90b6065cc", +"None": "937a574f550c49d90b6065cc", +"bigquery": "937a574f550c49d90b6065cc", +"databricks": "937a574f550c49d90b6065cc", +"duckdb": "937a574f550c49d90b6065cc", +"generic": "937a574f550c49d90b6065cc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "937a574f550c49d90b6065cc", +"postgres": "937a574f550c49d90b6065cc", +"postgresql": "937a574f550c49d90b6065cc", +"snowflake": "937a574f550c49d90b6065cc", +"tsql": "937a574f550c49d90b6065cc" +} +], +[ +"type", +{ +"DuckDB": "ce156bc49d99761b01d1c550", +"None": "ce156bc49d99761b01d1c550", +"bigquery": "ce156bc49d99761b01d1c550", +"databricks": "ce156bc49d99761b01d1c550", +"duckdb": "ce156bc49d99761b01d1c550", +"generic": "ce156bc49d99761b01d1c550", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ce156bc49d99761b01d1c550", +"postgres": "ce156bc49d99761b01d1c550", +"postgresql": "ce156bc49d99761b01d1c550", +"snowflake": "ce156bc49d99761b01d1c550", +"tsql": "ce156bc49d99761b01d1c550" +} +], +[ +"type", +{ +"DuckDB": "2cc8cf0f153f72622d15048c", +"None": "2cc8cf0f153f72622d15048c", +"bigquery": "2cc8cf0f153f72622d15048c", +"databricks": "2cc8cf0f153f72622d15048c", +"duckdb": "2cc8cf0f153f72622d15048c", +"generic": "2cc8cf0f153f72622d15048c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2cc8cf0f153f72622d15048c", +"postgres": "2cc8cf0f153f72622d15048c", +"postgresql": "2cc8cf0f153f72622d15048c", +"snowflake": "2cc8cf0f153f72622d15048c", +"tsql": "2cc8cf0f153f72622d15048c" +} +], +[ +"type", +{ +"DuckDB": "ba72bc162859c1861cc127e7", +"None": "ba72bc162859c1861cc127e7", +"bigquery": "ba72bc162859c1861cc127e7", +"databricks": "ba72bc162859c1861cc127e7", +"duckdb": "ba72bc162859c1861cc127e7", +"generic": "ba72bc162859c1861cc127e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ba72bc162859c1861cc127e7", +"postgres": "ba72bc162859c1861cc127e7", +"postgresql": "ba72bc162859c1861cc127e7", +"snowflake": "ba72bc162859c1861cc127e7", +"tsql": "ba72bc162859c1861cc127e7" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "c2a3a3c3789aeea50e8c4f35", +"None": "c2a3a3c3789aeea50e8c4f35", +"bigquery": "c2a3a3c3789aeea50e8c4f35", +"databricks": "c2a3a3c3789aeea50e8c4f35", +"duckdb": "c2a3a3c3789aeea50e8c4f35", +"generic": "c2a3a3c3789aeea50e8c4f35", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c2a3a3c3789aeea50e8c4f35", +"postgres": "c2a3a3c3789aeea50e8c4f35", +"postgresql": "c2a3a3c3789aeea50e8c4f35", +"snowflake": "c2a3a3c3789aeea50e8c4f35", +"tsql": "c2a3a3c3789aeea50e8c4f35" +} +], +[ +"type", +{ +"DuckDB": "16c4b7f3bdc7c21a00a78838", +"None": "054898738769701df3abbf2e", +"bigquery": "054898738769701df3abbf2e", +"databricks": "054898738769701df3abbf2e", +"duckdb": "16c4b7f3bdc7c21a00a78838", +"generic": "054898738769701df3abbf2e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "054898738769701df3abbf2e", +"postgres": "054898738769701df3abbf2e", +"postgresql": "054898738769701df3abbf2e", +"snowflake": "054898738769701df3abbf2e", +"tsql": "054898738769701df3abbf2e" +} +], +[ +"type", +{ +"DuckDB": "b0b09c4a6f7570ece56f59ce", +"None": "b0b09c4a6f7570ece56f59ce", +"bigquery": "d9a4e245156e760303573500", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "b0b09c4a6f7570ece56f59ce", +"generic": "b0b09c4a6f7570ece56f59ce", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "355e032cc5c0f24695836b3d", +"postgres": "b0b09c4a6f7570ece56f59ce", +"postgresql": "b0b09c4a6f7570ece56f59ce", +"snowflake": "355e032cc5c0f24695836b3d", +"tsql": "b0b09c4a6f7570ece56f59ce" +} +], +[ +"type", +{ +"DuckDB": "e8a5e051bb567983c365b3fd", +"None": "e8a5e051bb567983c365b3fd", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "e8a5e051bb567983c365b3fd", +"duckdb": "e8a5e051bb567983c365b3fd", +"generic": "e8a5e051bb567983c365b3fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8a5e051bb567983c365b3fd", +"postgres": "e8a5e051bb567983c365b3fd", +"postgresql": "e8a5e051bb567983c365b3fd", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "e8a5e051bb567983c365b3fd" +} +], +[ +"type", +{ +"DuckDB": "50563fc19d4ed8ceb4f5e8e4", +"None": "50563fc19d4ed8ceb4f5e8e4", +"bigquery": "b871f531d5fa48f344a9756b", +"databricks": "207b6dc5f2a2547137393792", +"duckdb": "50563fc19d4ed8ceb4f5e8e4", +"generic": "50563fc19d4ed8ceb4f5e8e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "50563fc19d4ed8ceb4f5e8e4", +"postgres": "8770742a4fe26efe6bd47a00", +"postgresql": "8770742a4fe26efe6bd47a00", +"snowflake": "a4cd3eca76c356ecd633fe7b", +"tsql": "50563fc19d4ed8ceb4f5e8e4" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "77ffdb80029bd6e2cca768ad", +"None": "77ffdb80029bd6e2cca768ad", +"bigquery": "77ffdb80029bd6e2cca768ad", +"databricks": "77ffdb80029bd6e2cca768ad", +"duckdb": "77ffdb80029bd6e2cca768ad", +"generic": "77ffdb80029bd6e2cca768ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "77ffdb80029bd6e2cca768ad", +"postgres": "77ffdb80029bd6e2cca768ad", +"postgresql": "77ffdb80029bd6e2cca768ad", +"snowflake": "77ffdb80029bd6e2cca768ad", +"tsql": "77ffdb80029bd6e2cca768ad" +} +], +[ +"type", +{ +"DuckDB": "c37de6584758b2433c289aee", +"None": "c37de6584758b2433c289aee", +"bigquery": "c37de6584758b2433c289aee", +"databricks": "c37de6584758b2433c289aee", +"duckdb": "c37de6584758b2433c289aee", +"generic": "c37de6584758b2433c289aee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c37de6584758b2433c289aee", +"postgres": "c37de6584758b2433c289aee", +"postgresql": "c37de6584758b2433c289aee", +"snowflake": "c37de6584758b2433c289aee", +"tsql": "c37de6584758b2433c289aee" +} +], +[ +"type", +{ +"DuckDB": "555111267818cbd3d0b07105", +"None": "555111267818cbd3d0b07105", +"bigquery": "555111267818cbd3d0b07105", +"databricks": "555111267818cbd3d0b07105", +"duckdb": "555111267818cbd3d0b07105", +"generic": "555111267818cbd3d0b07105", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "555111267818cbd3d0b07105", +"postgres": "555111267818cbd3d0b07105", +"postgresql": "555111267818cbd3d0b07105", +"snowflake": "555111267818cbd3d0b07105", +"tsql": "555111267818cbd3d0b07105" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "42d4a23d9a52e79d0c3458d3", +"None": "3c0719ca5c83e0a20d0b0b42", +"bigquery": "3c0719ca5c83e0a20d0b0b42", +"databricks": "3c0719ca5c83e0a20d0b0b42", +"duckdb": "42d4a23d9a52e79d0c3458d3", +"generic": "3c0719ca5c83e0a20d0b0b42", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3c0719ca5c83e0a20d0b0b42", +"postgres": "3c0719ca5c83e0a20d0b0b42", +"postgresql": "3c0719ca5c83e0a20d0b0b42", +"snowflake": "a3acd7ba35e81a800f967bd3", +"tsql": "3c0719ca5c83e0a20d0b0b42" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "919b7ec6a7b49d8527b784cd", +"None": "919b7ec6a7b49d8527b784cd", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "919b7ec6a7b49d8527b784cd", +"generic": "919b7ec6a7b49d8527b784cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "919b7ec6a7b49d8527b784cd" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "5fd024c2193505b62de9c162", +"None": "5fd024c2193505b62de9c162", +"bigquery": "5fd024c2193505b62de9c162", +"databricks": "5fd024c2193505b62de9c162", +"duckdb": "5fd024c2193505b62de9c162", +"generic": "5fd024c2193505b62de9c162", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5fd024c2193505b62de9c162", +"postgres": "5fd024c2193505b62de9c162", +"postgresql": "5fd024c2193505b62de9c162", +"snowflake": "5fd024c2193505b62de9c162", +"tsql": "5fd024c2193505b62de9c162" +} +], +[ +"type", +{ +"DuckDB": "4b356542044f1267fcfc487a", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "4b356542044f1267fcfc487a", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "b0791f9f270e764eeeb6fb8e", +"None": "c7c38fdeaeb7858a44976f1d", +"bigquery": "cfb4a49a57197b0df1b2e905", +"databricks": "6bd27be6a47ef34ec1defbad", +"duckdb": "b0791f9f270e764eeeb6fb8e", +"generic": "c7c38fdeaeb7858a44976f1d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c7c38fdeaeb7858a44976f1d", +"postgres": "4d7604d41e3585ec187dec82", +"postgresql": "4d7604d41e3585ec187dec82", +"snowflake": "c7c38fdeaeb7858a44976f1d", +"tsql": "b0791f9f270e764eeeb6fb8e" +} +], +[ +"type", +{ +"DuckDB": "66bffd006fc7638db82257d2", +"None": "66bffd006fc7638db82257d2", +"bigquery": "66bffd006fc7638db82257d2", +"databricks": "66bffd006fc7638db82257d2", +"duckdb": "66bffd006fc7638db82257d2", +"generic": "66bffd006fc7638db82257d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "66bffd006fc7638db82257d2", +"postgres": "66bffd006fc7638db82257d2", +"postgresql": "66bffd006fc7638db82257d2", +"snowflake": "66bffd006fc7638db82257d2", +"tsql": "66bffd006fc7638db82257d2" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "4c4563a9f306a1bb11e116d9", +"None": "4c4563a9f306a1bb11e116d9", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "4c4563a9f306a1bb11e116d9", +"duckdb": "4c4563a9f306a1bb11e116d9", +"generic": "4c4563a9f306a1bb11e116d9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4c4563a9f306a1bb11e116d9", +"postgres": "4c4563a9f306a1bb11e116d9", +"postgresql": "4c4563a9f306a1bb11e116d9", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "4c4563a9f306a1bb11e116d9" +} +], +[ +"type", +{ +"DuckDB": "52928fec41d9097d054cb63d", +"None": "52928fec41d9097d054cb63d", +"bigquery": "52928fec41d9097d054cb63d", +"databricks": "52928fec41d9097d054cb63d", +"duckdb": "52928fec41d9097d054cb63d", +"generic": "52928fec41d9097d054cb63d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "52928fec41d9097d054cb63d", +"postgres": "52928fec41d9097d054cb63d", +"postgresql": "52928fec41d9097d054cb63d", +"snowflake": "52928fec41d9097d054cb63d", +"tsql": "52928fec41d9097d054cb63d" +} +], +[ +"type", +{ +"DuckDB": "787213ce6b6e210ded1d752c", +"None": "787213ce6b6e210ded1d752c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "787213ce6b6e210ded1d752c", +"duckdb": "787213ce6b6e210ded1d752c", +"generic": "787213ce6b6e210ded1d752c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "787213ce6b6e210ded1d752c", +"postgres": "787213ce6b6e210ded1d752c", +"postgresql": "787213ce6b6e210ded1d752c", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "787213ce6b6e210ded1d752c" +} +], +[ +"type", +{ +"DuckDB": "c6654395c62d21b415299f72", +"None": "c6654395c62d21b415299f72", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "c6654395c62d21b415299f72", +"duckdb": "c6654395c62d21b415299f72", +"generic": "c6654395c62d21b415299f72", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2978a8fe925c7f93c6e9fcfb", +"postgres": "c6654395c62d21b415299f72", +"postgresql": "c6654395c62d21b415299f72", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "c6654395c62d21b415299f72" +} +], +[ +"type", +{ +"DuckDB": "2ffee1a4ca7dc4a711fc0d27", +"None": "2ffee1a4ca7dc4a711fc0d27", +"bigquery": "2ffee1a4ca7dc4a711fc0d27", +"databricks": "2ffee1a4ca7dc4a711fc0d27", +"duckdb": "2ffee1a4ca7dc4a711fc0d27", +"generic": "2ffee1a4ca7dc4a711fc0d27", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2ffee1a4ca7dc4a711fc0d27", +"postgres": "2ffee1a4ca7dc4a711fc0d27", +"postgresql": "2ffee1a4ca7dc4a711fc0d27", +"snowflake": "2ffee1a4ca7dc4a711fc0d27", +"tsql": "2ffee1a4ca7dc4a711fc0d27" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "9c2cff8be7225cf79883ca65", +"None": "9c2cff8be7225cf79883ca65", +"bigquery": "9c2cff8be7225cf79883ca65", +"databricks": "901ac89f8d9ec18bd3a61e4b", +"duckdb": "9c2cff8be7225cf79883ca65", +"generic": "9c2cff8be7225cf79883ca65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "901ac89f8d9ec18bd3a61e4b", +"postgres": "9c2cff8be7225cf79883ca65", +"postgresql": "9c2cff8be7225cf79883ca65", +"snowflake": "9c2cff8be7225cf79883ca65", +"tsql": "9c2cff8be7225cf79883ca65" +} +], +[ +"type", +{ +"DuckDB": "05ebf79d18b2ae4eaa41f450", +"None": "ab62f43260f32e2cf62666fc", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "ab62f43260f32e2cf62666fc", +"duckdb": "05ebf79d18b2ae4eaa41f450", +"generic": "ab62f43260f32e2cf62666fc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab62f43260f32e2cf62666fc", +"postgres": "ab62f43260f32e2cf62666fc", +"postgresql": "ab62f43260f32e2cf62666fc", +"snowflake": "ab62f43260f32e2cf62666fc", +"tsql": "ab62f43260f32e2cf62666fc" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "97e5335218efead1a4226b8d", +"None": "97e5335218efead1a4226b8d", +"bigquery": "97e5335218efead1a4226b8d", +"databricks": "97e5335218efead1a4226b8d", +"duckdb": "97e5335218efead1a4226b8d", +"generic": "97e5335218efead1a4226b8d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "97e5335218efead1a4226b8d", +"postgres": "97e5335218efead1a4226b8d", +"postgresql": "97e5335218efead1a4226b8d", +"snowflake": "97e5335218efead1a4226b8d", +"tsql": "97e5335218efead1a4226b8d" +} +], +[ +"type", +{ +"DuckDB": "ee47e1424cb01deb0cf51760", +"None": "ee47e1424cb01deb0cf51760", +"bigquery": "ee47e1424cb01deb0cf51760", +"databricks": "ee47e1424cb01deb0cf51760", +"duckdb": "ee47e1424cb01deb0cf51760", +"generic": "ee47e1424cb01deb0cf51760", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee47e1424cb01deb0cf51760", +"postgres": "ee47e1424cb01deb0cf51760", +"postgresql": "ee47e1424cb01deb0cf51760", +"snowflake": "ee47e1424cb01deb0cf51760", +"tsql": "ee47e1424cb01deb0cf51760" +} +], +[ +"type", +{ +"DuckDB": "2d4aea8dec5521e258596f3d", +"None": "2d4aea8dec5521e258596f3d", +"bigquery": "2d4aea8dec5521e258596f3d", +"databricks": "2d4aea8dec5521e258596f3d", +"duckdb": "2d4aea8dec5521e258596f3d", +"generic": "2d4aea8dec5521e258596f3d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2d4aea8dec5521e258596f3d", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "2d4aea8dec5521e258596f3d" +} +], +[ +"type", +{ +"DuckDB": "8917b70db141a02ac2e2bb1e", +"None": "8917b70db141a02ac2e2bb1e", +"bigquery": "8917b70db141a02ac2e2bb1e", +"databricks": "8917b70db141a02ac2e2bb1e", +"duckdb": "8917b70db141a02ac2e2bb1e", +"generic": "8917b70db141a02ac2e2bb1e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8917b70db141a02ac2e2bb1e", +"postgres": "8917b70db141a02ac2e2bb1e", +"postgresql": "8917b70db141a02ac2e2bb1e", +"snowflake": "8917b70db141a02ac2e2bb1e", +"tsql": "8917b70db141a02ac2e2bb1e" +} +], +[ +"type", +{ +"DuckDB": "ef480cafe4d260b70ce5214c", +"None": "ef480cafe4d260b70ce5214c", +"bigquery": "ef480cafe4d260b70ce5214c", +"databricks": "ef480cafe4d260b70ce5214c", +"duckdb": "ef480cafe4d260b70ce5214c", +"generic": "ef480cafe4d260b70ce5214c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ef480cafe4d260b70ce5214c", +"postgres": "ef480cafe4d260b70ce5214c", +"postgresql": "ef480cafe4d260b70ce5214c", +"snowflake": "ef480cafe4d260b70ce5214c", +"tsql": "ef480cafe4d260b70ce5214c" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "78b2cf275c640af54feeca31", +"bigquery": "78b2cf275c640af54feeca31", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "ee47e1424cb01deb0cf51760", +"None": "ee47e1424cb01deb0cf51760", +"bigquery": "ee47e1424cb01deb0cf51760", +"databricks": "ee47e1424cb01deb0cf51760", +"duckdb": "ee47e1424cb01deb0cf51760", +"generic": "ee47e1424cb01deb0cf51760", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee47e1424cb01deb0cf51760", +"postgres": "ee47e1424cb01deb0cf51760", +"postgresql": "ee47e1424cb01deb0cf51760", +"snowflake": "ee47e1424cb01deb0cf51760", +"tsql": "ee47e1424cb01deb0cf51760" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "61d08c878cc506dd91ca4586", +"None": "61d08c878cc506dd91ca4586", +"bigquery": "73f0a74496572926a274036e", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "61d08c878cc506dd91ca4586", +"generic": "61d08c878cc506dd91ca4586", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c100cdbd268164cf084295be", +"postgres": "61d08c878cc506dd91ca4586", +"postgresql": "61d08c878cc506dd91ca4586", +"snowflake": "c100cdbd268164cf084295be", +"tsql": "61d08c878cc506dd91ca4586" +} +], +[ +"type", +{ +"DuckDB": "755657fbcced1de4551fb370", +"None": "41ae0d23941c87be23d59e1f", +"bigquery": "41ae0d23941c87be23d59e1f", +"databricks": "41ae0d23941c87be23d59e1f", +"duckdb": "755657fbcced1de4551fb370", +"generic": "41ae0d23941c87be23d59e1f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "41ae0d23941c87be23d59e1f", +"postgres": "41ae0d23941c87be23d59e1f", +"postgresql": "41ae0d23941c87be23d59e1f", +"snowflake": "41ae0d23941c87be23d59e1f", +"tsql": "41ae0d23941c87be23d59e1f" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "251aba3fef503ce64556c701", +"postgresql": "251aba3fef503ce64556c701", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "251aba3fef503ce64556c701" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "c82815abe30b3d099903f838", +"None": "c82815abe30b3d099903f838", +"bigquery": "c82815abe30b3d099903f838", +"databricks": "c82815abe30b3d099903f838", +"duckdb": "c82815abe30b3d099903f838", +"generic": "c82815abe30b3d099903f838", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c82815abe30b3d099903f838", +"postgres": "c82815abe30b3d099903f838", +"postgresql": "c82815abe30b3d099903f838", +"snowflake": "c82815abe30b3d099903f838", +"tsql": "07ca53fdc57a22a78ad75787" +} +], +[ +"type", +{ +"DuckDB": "d6e37cd37419bfb26e09b080", +"None": "d6e37cd37419bfb26e09b080", +"bigquery": "d6e37cd37419bfb26e09b080", +"databricks": "d6e37cd37419bfb26e09b080", +"duckdb": "d6e37cd37419bfb26e09b080", +"generic": "d6e37cd37419bfb26e09b080", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6e37cd37419bfb26e09b080", +"postgres": "d6e37cd37419bfb26e09b080", +"postgresql": "d6e37cd37419bfb26e09b080", +"snowflake": "d6e37cd37419bfb26e09b080", +"tsql": "d6e37cd37419bfb26e09b080" +} +], +[ +"type", +{ +"DuckDB": "5c28019dce48a74708c90a76", +"None": "5c28019dce48a74708c90a76", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "5c28019dce48a74708c90a76", +"duckdb": "5c28019dce48a74708c90a76", +"generic": "5c28019dce48a74708c90a76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "5c28019dce48a74708c90a76", +"postgresql": "5c28019dce48a74708c90a76", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "2a7a7132adc795ca2bc39c37" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "9070740b5c7f7fb5d091ad05", +"None": "9070740b5c7f7fb5d091ad05", +"bigquery": "9070740b5c7f7fb5d091ad05", +"databricks": "9070740b5c7f7fb5d091ad05", +"duckdb": "9070740b5c7f7fb5d091ad05", +"generic": "9070740b5c7f7fb5d091ad05", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9070740b5c7f7fb5d091ad05", +"postgres": "9070740b5c7f7fb5d091ad05", +"postgresql": "9070740b5c7f7fb5d091ad05", +"snowflake": "9070740b5c7f7fb5d091ad05", +"tsql": "9070740b5c7f7fb5d091ad05" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "c0eec31620dfc2ac0028173d", +"None": "7f902bdd94be27212c27de4d", +"bigquery": "02e0b2ed25f32a8d09350a23", +"databricks": "7f902bdd94be27212c27de4d", +"duckdb": "c0eec31620dfc2ac0028173d", +"generic": "7f902bdd94be27212c27de4d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "6d562c54c27c6810625f7693", +"postgresql": "6d562c54c27c6810625f7693", +"snowflake": "354dee69d4f0c63cf79046ac", +"tsql": "bf34c743a59d7c3f212cf17b" +} +], +[ +"type", +{ +"DuckDB": "6cffd1617edd6454241ee732", +"None": "6cffd1617edd6454241ee732", +"bigquery": "6cffd1617edd6454241ee732", +"databricks": "6cffd1617edd6454241ee732", +"duckdb": "6cffd1617edd6454241ee732", +"generic": "6cffd1617edd6454241ee732", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6cffd1617edd6454241ee732", +"postgres": "6cffd1617edd6454241ee732", +"postgresql": "6cffd1617edd6454241ee732", +"snowflake": "6cffd1617edd6454241ee732", +"tsql": "6cffd1617edd6454241ee732" +} +], +[ +"type", +{ +"DuckDB": "111af35848fe500ec2ae54b2", +"None": "111af35848fe500ec2ae54b2", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "111af35848fe500ec2ae54b2", +"generic": "111af35848fe500ec2ae54b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e3bc1b8bad22d8104417b00f", +"postgres": "111af35848fe500ec2ae54b2", +"postgresql": "111af35848fe500ec2ae54b2", +"snowflake": "e3bc1b8bad22d8104417b00f", +"tsql": "111af35848fe500ec2ae54b2" +} +], +[ +"type", +{ +"DuckDB": "a766fea06fc070b352976b0a", +"None": "a766fea06fc070b352976b0a", +"bigquery": "e4f0244beaa01667e80482b6", +"databricks": "e4f0244beaa01667e80482b6", +"duckdb": "a766fea06fc070b352976b0a", +"generic": "a766fea06fc070b352976b0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e4f0244beaa01667e80482b6", +"postgres": "a766fea06fc070b352976b0a", +"postgresql": "a766fea06fc070b352976b0a", +"snowflake": "a766fea06fc070b352976b0a", +"tsql": "a766fea06fc070b352976b0a" +} +], +[ +"type", +{ +"DuckDB": "6a912271e85e6d85c1238bc0", +"None": "6a912271e85e6d85c1238bc0", +"bigquery": "6a912271e85e6d85c1238bc0", +"databricks": "6a912271e85e6d85c1238bc0", +"duckdb": "6a912271e85e6d85c1238bc0", +"generic": "6a912271e85e6d85c1238bc0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a912271e85e6d85c1238bc0", +"postgres": "6a912271e85e6d85c1238bc0", +"postgresql": "6a912271e85e6d85c1238bc0", +"snowflake": "6a912271e85e6d85c1238bc0", +"tsql": "6a912271e85e6d85c1238bc0" +} +], +[ +"type", +{ +"DuckDB": "3efe8bb57c98fcbe74096b3c", +"None": "3efe8bb57c98fcbe74096b3c", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "3efe8bb57c98fcbe74096b3c", +"generic": "3efe8bb57c98fcbe74096b3c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "3efe8bb57c98fcbe74096b3c" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "07c9e779f7ae9c58f4f0c902", +"None": "07c9e779f7ae9c58f4f0c902", +"bigquery": "07c9e779f7ae9c58f4f0c902", +"databricks": "07c9e779f7ae9c58f4f0c902", +"duckdb": "07c9e779f7ae9c58f4f0c902", +"generic": "07c9e779f7ae9c58f4f0c902", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "07c9e779f7ae9c58f4f0c902", +"postgres": "07c9e779f7ae9c58f4f0c902", +"postgresql": "07c9e779f7ae9c58f4f0c902", +"snowflake": "07c9e779f7ae9c58f4f0c902", +"tsql": "07c9e779f7ae9c58f4f0c902" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "5c28019dce48a74708c90a76", +"None": "5c28019dce48a74708c90a76", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "5c28019dce48a74708c90a76", +"duckdb": "5c28019dce48a74708c90a76", +"generic": "5c28019dce48a74708c90a76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "5c28019dce48a74708c90a76", +"postgresql": "5c28019dce48a74708c90a76", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "2a7a7132adc795ca2bc39c37" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "e248573edaaaa875cb926c45", +"None": "e248573edaaaa875cb926c45", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "e248573edaaaa875cb926c45", +"duckdb": "e248573edaaaa875cb926c45", +"generic": "e248573edaaaa875cb926c45", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e248573edaaaa875cb926c45", +"postgres": "e248573edaaaa875cb926c45", +"postgresql": "e248573edaaaa875cb926c45", +"snowflake": "e248573edaaaa875cb926c45", +"tsql": "e248573edaaaa875cb926c45" +} +], +[ +"type", +{ +"DuckDB": "0b8ad171901cc2b7725617da", +"None": "0b8ad171901cc2b7725617da", +"bigquery": "0b8ad171901cc2b7725617da", +"databricks": "0b8ad171901cc2b7725617da", +"duckdb": "0b8ad171901cc2b7725617da", +"generic": "0b8ad171901cc2b7725617da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b8ad171901cc2b7725617da", +"postgres": "0b8ad171901cc2b7725617da", +"postgresql": "0b8ad171901cc2b7725617da", +"snowflake": "0b8ad171901cc2b7725617da", +"tsql": "0b8ad171901cc2b7725617da" +} +], +[ +"type", +{ +"DuckDB": "d44c023c720f385769f2ab81", +"None": "d44c023c720f385769f2ab81", +"bigquery": "d44c023c720f385769f2ab81", +"databricks": "d44c023c720f385769f2ab81", +"duckdb": "d44c023c720f385769f2ab81", +"generic": "d44c023c720f385769f2ab81", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d44c023c720f385769f2ab81", +"postgres": "d44c023c720f385769f2ab81", +"postgresql": "d44c023c720f385769f2ab81", +"snowflake": "d44c023c720f385769f2ab81", +"tsql": "d44c023c720f385769f2ab81" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "c5c5fbb0257c93153cb84430", +"None": "c5c5fbb0257c93153cb84430", +"bigquery": "c5c5fbb0257c93153cb84430", +"databricks": "c5c5fbb0257c93153cb84430", +"duckdb": "c5c5fbb0257c93153cb84430", +"generic": "c5c5fbb0257c93153cb84430", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c5c5fbb0257c93153cb84430", +"postgres": "c5c5fbb0257c93153cb84430", +"postgresql": "c5c5fbb0257c93153cb84430", +"snowflake": "c5c5fbb0257c93153cb84430", +"tsql": "c5c5fbb0257c93153cb84430" +} +], +[ +"type", +{ +"DuckDB": "d6e37cd37419bfb26e09b080", +"None": "d6e37cd37419bfb26e09b080", +"bigquery": "d6e37cd37419bfb26e09b080", +"databricks": "d6e37cd37419bfb26e09b080", +"duckdb": "d6e37cd37419bfb26e09b080", +"generic": "d6e37cd37419bfb26e09b080", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6e37cd37419bfb26e09b080", +"postgres": "d6e37cd37419bfb26e09b080", +"postgresql": "d6e37cd37419bfb26e09b080", +"snowflake": "d6e37cd37419bfb26e09b080", +"tsql": "d6e37cd37419bfb26e09b080" +} +], +[ +"type", +{ +"DuckDB": "d34f84c1b71b9c9c0c20abb0", +"None": "d34f84c1b71b9c9c0c20abb0", +"bigquery": "d34f84c1b71b9c9c0c20abb0", +"databricks": "c662d9470aa4255dfb493df1", +"duckdb": "d34f84c1b71b9c9c0c20abb0", +"generic": "d34f84c1b71b9c9c0c20abb0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c662d9470aa4255dfb493df1", +"postgres": "d34f84c1b71b9c9c0c20abb0", +"postgresql": "d34f84c1b71b9c9c0c20abb0", +"snowflake": "d34f84c1b71b9c9c0c20abb0", +"tsql": "d34f84c1b71b9c9c0c20abb0" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "bbb2563edda71d0b69ef0855", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "bbb2563edda71d0b69ef0855", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "bbb2563edda71d0b69ef0855", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "51d717b08676179ee1ccefe4", +"postgres": "bbb2563edda71d0b69ef0855", +"postgresql": "bbb2563edda71d0b69ef0855", +"snowflake": "bbb2563edda71d0b69ef0855", +"tsql": "83fd3ed7282be0616dc3037e" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "c1eef7499ef79562fa125447", +"None": "c1eef7499ef79562fa125447", +"bigquery": "c1eef7499ef79562fa125447", +"databricks": "c1eef7499ef79562fa125447", +"duckdb": "c1eef7499ef79562fa125447", +"generic": "c1eef7499ef79562fa125447", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c1eef7499ef79562fa125447", +"postgres": "c1eef7499ef79562fa125447", +"postgresql": "c1eef7499ef79562fa125447", +"snowflake": "c1eef7499ef79562fa125447", +"tsql": "c1eef7499ef79562fa125447" +} +], +[ +"type", +{ +"DuckDB": "b210f8eb26c37b422f786c6b", +"None": "b210f8eb26c37b422f786c6b", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "b210f8eb26c37b422f786c6b", +"duckdb": "b210f8eb26c37b422f786c6b", +"generic": "b210f8eb26c37b422f786c6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b210f8eb26c37b422f786c6b", +"postgres": "b210f8eb26c37b422f786c6b", +"postgresql": "b210f8eb26c37b422f786c6b", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "b210f8eb26c37b422f786c6b" +} +], +[ +"type", +{ +"DuckDB": "2d4aea8dec5521e258596f3d", +"None": "2d4aea8dec5521e258596f3d", +"bigquery": "2d4aea8dec5521e258596f3d", +"databricks": "2d4aea8dec5521e258596f3d", +"duckdb": "2d4aea8dec5521e258596f3d", +"generic": "2d4aea8dec5521e258596f3d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2d4aea8dec5521e258596f3d", +"postgres": "f15c56475abfc73488abe63b", +"postgresql": "f15c56475abfc73488abe63b", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "2d4aea8dec5521e258596f3d" +} +], +[ +"type", +{ +"DuckDB": "b4d65f2c9d0405e88d28938b", +"None": "ba98b19402ac6379a4172de9", +"bigquery": "ba98b19402ac6379a4172de9", +"databricks": "ba98b19402ac6379a4172de9", +"duckdb": "b4d65f2c9d0405e88d28938b", +"generic": "ba98b19402ac6379a4172de9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ba98b19402ac6379a4172de9", +"postgres": "ba98b19402ac6379a4172de9", +"postgresql": "ba98b19402ac6379a4172de9", +"snowflake": "ba98b19402ac6379a4172de9", +"tsql": "ba98b19402ac6379a4172de9" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "6c58c6a62958e64e6a26bf9c", +"None": "6c58c6a62958e64e6a26bf9c", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "6c58c6a62958e64e6a26bf9c", +"duckdb": "6c58c6a62958e64e6a26bf9c", +"generic": "6c58c6a62958e64e6a26bf9c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c58c6a62958e64e6a26bf9c", +"postgres": "571886088d0423ed1a0f1696", +"postgresql": "571886088d0423ed1a0f1696", +"snowflake": "6c58c6a62958e64e6a26bf9c", +"tsql": "919b7ec6a7b49d8527b784cd" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "42a76d5ff05d0f91911ff368", +"None": "42a76d5ff05d0f91911ff368", +"bigquery": "42a76d5ff05d0f91911ff368", +"databricks": "42a76d5ff05d0f91911ff368", +"duckdb": "42a76d5ff05d0f91911ff368", +"generic": "42a76d5ff05d0f91911ff368", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42a76d5ff05d0f91911ff368", +"postgres": "42a76d5ff05d0f91911ff368", +"postgresql": "42a76d5ff05d0f91911ff368", +"snowflake": "42a76d5ff05d0f91911ff368", +"tsql": "42a76d5ff05d0f91911ff368" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "251aba3fef503ce64556c701", +"postgresql": "251aba3fef503ce64556c701", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "251aba3fef503ce64556c701" +} +], +[ +"type", +{ +"DuckDB": "5d25d5e13f81700741695ed4", +"None": "5d25d5e13f81700741695ed4", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "5d25d5e13f81700741695ed4", +"duckdb": "5d25d5e13f81700741695ed4", +"generic": "5d25d5e13f81700741695ed4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5d25d5e13f81700741695ed4", +"postgres": "5d25d5e13f81700741695ed4", +"postgresql": "5d25d5e13f81700741695ed4", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "5d25d5e13f81700741695ed4" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "1e75e5227bea0ae294ac7af1", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "1e75e5227bea0ae294ac7af1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "19bdd92ea0cc5fb028657510", +"postgresql": "19bdd92ea0cc5fb028657510", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "901e8c830028411846541f65" +} +], +[ +"type", +{ +"DuckDB": "268b9b7c7862c9de6569286a", +"None": "268b9b7c7862c9de6569286a", +"bigquery": "268b9b7c7862c9de6569286a", +"databricks": "268b9b7c7862c9de6569286a", +"duckdb": "268b9b7c7862c9de6569286a", +"generic": "268b9b7c7862c9de6569286a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "268b9b7c7862c9de6569286a", +"postgres": "268b9b7c7862c9de6569286a", +"postgresql": "268b9b7c7862c9de6569286a", +"snowflake": "268b9b7c7862c9de6569286a", +"tsql": "268b9b7c7862c9de6569286a" +} +], +[ +"type", +{ +"DuckDB": "da33de41a707d0e067e415fd", +"None": "da33de41a707d0e067e415fd", +"bigquery": "da33de41a707d0e067e415fd", +"databricks": "da33de41a707d0e067e415fd", +"duckdb": "da33de41a707d0e067e415fd", +"generic": "da33de41a707d0e067e415fd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "da33de41a707d0e067e415fd", +"postgres": "da33de41a707d0e067e415fd", +"postgresql": "da33de41a707d0e067e415fd", +"snowflake": "da33de41a707d0e067e415fd", +"tsql": "da33de41a707d0e067e415fd" +} +], +[ +"type", +{ +"DuckDB": "7c8df74df226e73b459bfdba", +"None": "7c8df74df226e73b459bfdba", +"bigquery": "7c8df74df226e73b459bfdba", +"databricks": "7c8df74df226e73b459bfdba", +"duckdb": "7c8df74df226e73b459bfdba", +"generic": "7c8df74df226e73b459bfdba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7c8df74df226e73b459bfdba", +"postgres": "7c8df74df226e73b459bfdba", +"postgresql": "7c8df74df226e73b459bfdba", +"snowflake": "7c8df74df226e73b459bfdba", +"tsql": "7c8df74df226e73b459bfdba" +} +], +[ +"type", +{ +"DuckDB": "ebb346128c80cb653e9694c8", +"None": "ebb346128c80cb653e9694c8", +"bigquery": "ebb346128c80cb653e9694c8", +"databricks": "ebb346128c80cb653e9694c8", +"duckdb": "ebb346128c80cb653e9694c8", +"generic": "ebb346128c80cb653e9694c8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebb346128c80cb653e9694c8", +"postgres": "ebb346128c80cb653e9694c8", +"postgresql": "ebb346128c80cb653e9694c8", +"snowflake": "ebb346128c80cb653e9694c8", +"tsql": "ebb346128c80cb653e9694c8" +} +], +[ +"type", +{ +"DuckDB": "9f4b51f000ffbe30f6b7d826", +"None": "bacfe825b19ef52bc792ab22", +"bigquery": "00714a615c9e7a090fb122c5", +"databricks": "00714a615c9e7a090fb122c5", +"duckdb": "9f4b51f000ffbe30f6b7d826", +"generic": "bacfe825b19ef52bc792ab22", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "00714a615c9e7a090fb122c5", +"postgres": "bacfe825b19ef52bc792ab22", +"postgresql": "bacfe825b19ef52bc792ab22", +"snowflake": "bacfe825b19ef52bc792ab22", +"tsql": "bacfe825b19ef52bc792ab22" +} +], +[ +"type", +{ +"DuckDB": "f85dfff687194a44f32eeb15", +"None": "f85dfff687194a44f32eeb15", +"bigquery": "f85dfff687194a44f32eeb15", +"databricks": "f85dfff687194a44f32eeb15", +"duckdb": "f85dfff687194a44f32eeb15", +"generic": "f85dfff687194a44f32eeb15", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f85dfff687194a44f32eeb15", +"postgres": "f85dfff687194a44f32eeb15", +"postgresql": "f85dfff687194a44f32eeb15", +"snowflake": "f85dfff687194a44f32eeb15", +"tsql": "f85dfff687194a44f32eeb15" +} +], +[ +"type", +{ +"DuckDB": "24b677b214ecdacdb007642e", +"None": "24b677b214ecdacdb007642e", +"bigquery": "24b677b214ecdacdb007642e", +"databricks": "24b677b214ecdacdb007642e", +"duckdb": "24b677b214ecdacdb007642e", +"generic": "24b677b214ecdacdb007642e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "24b677b214ecdacdb007642e", +"postgres": "24b677b214ecdacdb007642e", +"postgresql": "24b677b214ecdacdb007642e", +"snowflake": "24b677b214ecdacdb007642e", +"tsql": "24b677b214ecdacdb007642e" +} +], +[ +"type", +{ +"DuckDB": "4c2128e6078118fd0d3856fa", +"None": "4c2128e6078118fd0d3856fa", +"bigquery": "4c2128e6078118fd0d3856fa", +"databricks": "4c2128e6078118fd0d3856fa", +"duckdb": "4c2128e6078118fd0d3856fa", +"generic": "4c2128e6078118fd0d3856fa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4c2128e6078118fd0d3856fa", +"postgres": "4c2128e6078118fd0d3856fa", +"postgresql": "4c2128e6078118fd0d3856fa", +"snowflake": "4c2128e6078118fd0d3856fa", +"tsql": "4c2128e6078118fd0d3856fa" +} +], +[ +"type", +{ +"DuckDB": "8698d724534891869075175e", +"None": "0017a88bb60ec3403c786ed8", +"bigquery": "2795066c9a71fa3e3780c73b", +"databricks": "0017a88bb60ec3403c786ed8", +"duckdb": "8698d724534891869075175e", +"generic": "0017a88bb60ec3403c786ed8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8698d724534891869075175e", +"postgresql": "8698d724534891869075175e", +"snowflake": "b3b752c645651b849e23d39a", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "b1b23029bedaa16e2a6f8516", +"None": "b1b23029bedaa16e2a6f8516", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "b1b23029bedaa16e2a6f8516", +"duckdb": "b1b23029bedaa16e2a6f8516", +"generic": "b1b23029bedaa16e2a6f8516", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b1b23029bedaa16e2a6f8516", +"postgres": "b1b23029bedaa16e2a6f8516", +"postgresql": "b1b23029bedaa16e2a6f8516", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "b1b23029bedaa16e2a6f8516" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "e7b8de8bd235305d79e2b6b1", +"None": "e7b8de8bd235305d79e2b6b1", +"bigquery": "e7b8de8bd235305d79e2b6b1", +"databricks": "e7b8de8bd235305d79e2b6b1", +"duckdb": "e7b8de8bd235305d79e2b6b1", +"generic": "e7b8de8bd235305d79e2b6b1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7b8de8bd235305d79e2b6b1", +"postgres": "e7b8de8bd235305d79e2b6b1", +"postgresql": "e7b8de8bd235305d79e2b6b1", +"snowflake": "e7b8de8bd235305d79e2b6b1", +"tsql": "e7b8de8bd235305d79e2b6b1" +} +], +[ +"type", +{ +"DuckDB": "9aff7fcc7d550e1a8d105471", +"None": "9aff7fcc7d550e1a8d105471", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "9aff7fcc7d550e1a8d105471", +"duckdb": "9aff7fcc7d550e1a8d105471", +"generic": "9aff7fcc7d550e1a8d105471", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9aff7fcc7d550e1a8d105471", +"postgres": "9aff7fcc7d550e1a8d105471", +"postgresql": "9aff7fcc7d550e1a8d105471", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "9aff7fcc7d550e1a8d105471" +} +], +[ +"type", +{ +"DuckDB": "242bf50062e95bc8534ca887", +"None": "242bf50062e95bc8534ca887", +"bigquery": "242bf50062e95bc8534ca887", +"databricks": "242bf50062e95bc8534ca887", +"duckdb": "242bf50062e95bc8534ca887", +"generic": "242bf50062e95bc8534ca887", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "242bf50062e95bc8534ca887", +"postgres": "242bf50062e95bc8534ca887", +"postgresql": "242bf50062e95bc8534ca887", +"snowflake": "242bf50062e95bc8534ca887", +"tsql": "242bf50062e95bc8534ca887" +} +], +[ +"type", +{ +"DuckDB": "23ee196b7bd6039855a1460f", +"None": "23ee196b7bd6039855a1460f", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "23ee196b7bd6039855a1460f", +"duckdb": "23ee196b7bd6039855a1460f", +"generic": "23ee196b7bd6039855a1460f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23ee196b7bd6039855a1460f", +"postgres": "23ee196b7bd6039855a1460f", +"postgresql": "23ee196b7bd6039855a1460f", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "23ee196b7bd6039855a1460f" +} +], +[ +"type", +{ +"DuckDB": "13f1401e7cd410600a4c9485", +"None": "13f1401e7cd410600a4c9485", +"bigquery": "74a30f6aa9def0b58afc616c", +"databricks": "13f1401e7cd410600a4c9485", +"duckdb": "13f1401e7cd410600a4c9485", +"generic": "13f1401e7cd410600a4c9485", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "13f1401e7cd410600a4c9485", +"postgres": "13f1401e7cd410600a4c9485", +"postgresql": "13f1401e7cd410600a4c9485", +"snowflake": "9b5f44707b5f4764b82741be", +"tsql": "13f1401e7cd410600a4c9485" +} +], +[ +"type", +{ +"DuckDB": "e1554f02dea662c971e7b9b9", +"None": "6f851103102e7b986535376f", +"bigquery": "58b7e0908df666938398205f", +"databricks": "6f851103102e7b986535376f", +"duckdb": "e1554f02dea662c971e7b9b9", +"generic": "6f851103102e7b986535376f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1a853242fb56557e2865a86a", +"postgres": "a0b34c5b4e5ace89b22c6971", +"postgresql": "a0b34c5b4e5ace89b22c6971", +"snowflake": "87800133cac38d9119180c0a", +"tsql": "0e9ef5373e5d52970e8e1656" +} +], +[ +"type", +{ +"DuckDB": "15be07c71f6871ae037caae3", +"None": "15be07c71f6871ae037caae3", +"bigquery": "15be07c71f6871ae037caae3", +"databricks": "15be07c71f6871ae037caae3", +"duckdb": "15be07c71f6871ae037caae3", +"generic": "15be07c71f6871ae037caae3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "15be07c71f6871ae037caae3", +"postgres": "15be07c71f6871ae037caae3", +"postgresql": "15be07c71f6871ae037caae3", +"snowflake": "15be07c71f6871ae037caae3", +"tsql": "15be07c71f6871ae037caae3" +} +], +[ +"type", +{ +"DuckDB": "b210f8eb26c37b422f786c6b", +"None": "b210f8eb26c37b422f786c6b", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "b210f8eb26c37b422f786c6b", +"duckdb": "b210f8eb26c37b422f786c6b", +"generic": "b210f8eb26c37b422f786c6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b210f8eb26c37b422f786c6b", +"postgres": "b210f8eb26c37b422f786c6b", +"postgresql": "b210f8eb26c37b422f786c6b", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "b210f8eb26c37b422f786c6b" +} +], +[ +"type", +{ +"DuckDB": "3d23f811a03db519b598c815", +"None": "3d23f811a03db519b598c815", +"bigquery": "3d23f811a03db519b598c815", +"databricks": "3d23f811a03db519b598c815", +"duckdb": "3d23f811a03db519b598c815", +"generic": "3d23f811a03db519b598c815", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d23f811a03db519b598c815", +"postgres": "3d23f811a03db519b598c815", +"postgresql": "3d23f811a03db519b598c815", +"snowflake": "3d23f811a03db519b598c815", +"tsql": "3d23f811a03db519b598c815" +} +], +[ +"type", +{ +"DuckDB": "fa55698f69d154f89214f351", +"None": "fa55698f69d154f89214f351", +"bigquery": "fa55698f69d154f89214f351", +"databricks": "fa55698f69d154f89214f351", +"duckdb": "fa55698f69d154f89214f351", +"generic": "fa55698f69d154f89214f351", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fa55698f69d154f89214f351", +"postgres": "fa55698f69d154f89214f351", +"postgresql": "fa55698f69d154f89214f351", +"snowflake": "fa55698f69d154f89214f351", +"tsql": "fa55698f69d154f89214f351" +} +], +[ +"type", +{ +"DuckDB": "3009be83c84cdfc92bbb075e", +"None": "3009be83c84cdfc92bbb075e", +"bigquery": "3009be83c84cdfc92bbb075e", +"databricks": "3009be83c84cdfc92bbb075e", +"duckdb": "3009be83c84cdfc92bbb075e", +"generic": "3009be83c84cdfc92bbb075e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3009be83c84cdfc92bbb075e", +"postgres": "3009be83c84cdfc92bbb075e", +"postgresql": "3009be83c84cdfc92bbb075e", +"snowflake": "3009be83c84cdfc92bbb075e", +"tsql": "3009be83c84cdfc92bbb075e" +} +], +[ +"type", +{ +"DuckDB": "23ee196b7bd6039855a1460f", +"None": "23ee196b7bd6039855a1460f", +"bigquery": "23ee196b7bd6039855a1460f", +"databricks": "23ee196b7bd6039855a1460f", +"duckdb": "23ee196b7bd6039855a1460f", +"generic": "23ee196b7bd6039855a1460f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23ee196b7bd6039855a1460f", +"postgres": "23ee196b7bd6039855a1460f", +"postgresql": "23ee196b7bd6039855a1460f", +"snowflake": "5d25d5e13f81700741695ed4", +"tsql": "23ee196b7bd6039855a1460f" +} +], +[ +"type", +{ +"DuckDB": "9d6807d797be08f8376a5a19", +"None": "9d6807d797be08f8376a5a19", +"bigquery": "9d6807d797be08f8376a5a19", +"databricks": "9d6807d797be08f8376a5a19", +"duckdb": "9d6807d797be08f8376a5a19", +"generic": "9d6807d797be08f8376a5a19", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9d6807d797be08f8376a5a19", +"postgres": "9d6807d797be08f8376a5a19", +"postgresql": "9d6807d797be08f8376a5a19", +"snowflake": "9d6807d797be08f8376a5a19", +"tsql": "9d6807d797be08f8376a5a19" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "100c78a43fb3cbda50787a10", +"None": "100c78a43fb3cbda50787a10", +"bigquery": "100c78a43fb3cbda50787a10", +"databricks": "100c78a43fb3cbda50787a10", +"duckdb": "100c78a43fb3cbda50787a10", +"generic": "100c78a43fb3cbda50787a10", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "100c78a43fb3cbda50787a10", +"postgres": "100c78a43fb3cbda50787a10", +"postgresql": "100c78a43fb3cbda50787a10", +"snowflake": "100c78a43fb3cbda50787a10", +"tsql": "100c78a43fb3cbda50787a10" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "624eb95d280fa0653ec73f7f", +"None": "624eb95d280fa0653ec73f7f", +"bigquery": "624eb95d280fa0653ec73f7f", +"databricks": "624eb95d280fa0653ec73f7f", +"duckdb": "624eb95d280fa0653ec73f7f", +"generic": "624eb95d280fa0653ec73f7f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "624eb95d280fa0653ec73f7f", +"postgres": "624eb95d280fa0653ec73f7f", +"postgresql": "624eb95d280fa0653ec73f7f", +"snowflake": "624eb95d280fa0653ec73f7f", +"tsql": "624eb95d280fa0653ec73f7f" +} +], +[ +"type", +{ +"DuckDB": "6b04f574865e71733e09ba1d", +"None": "6b04f574865e71733e09ba1d", +"bigquery": "6b04f574865e71733e09ba1d", +"databricks": "6b04f574865e71733e09ba1d", +"duckdb": "6b04f574865e71733e09ba1d", +"generic": "6b04f574865e71733e09ba1d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6b04f574865e71733e09ba1d", +"postgres": "6b04f574865e71733e09ba1d", +"postgresql": "6b04f574865e71733e09ba1d", +"snowflake": "6b04f574865e71733e09ba1d", +"tsql": "6b04f574865e71733e09ba1d" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "4062ec81199547543ab068fb", +"None": "4062ec81199547543ab068fb", +"bigquery": "7009fc15fd2c35608ab16caf", +"databricks": "0739784ba4c53d23afd45a6b", +"duckdb": "4062ec81199547543ab068fb", +"generic": "4062ec81199547543ab068fb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4062ec81199547543ab068fb", +"postgres": "4062ec81199547543ab068fb", +"postgresql": "4062ec81199547543ab068fb", +"snowflake": "0739784ba4c53d23afd45a6b", +"tsql": "0739784ba4c53d23afd45a6b" +} +], +[ +"type", +{ +"DuckDB": "07f8200cee37aa0f6990da3d", +"None": "07f8200cee37aa0f6990da3d", +"bigquery": "07f8200cee37aa0f6990da3d", +"databricks": "07f8200cee37aa0f6990da3d", +"duckdb": "07f8200cee37aa0f6990da3d", +"generic": "07f8200cee37aa0f6990da3d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "07f8200cee37aa0f6990da3d", +"postgres": "07f8200cee37aa0f6990da3d", +"postgresql": "07f8200cee37aa0f6990da3d", +"snowflake": "07f8200cee37aa0f6990da3d", +"tsql": "07f8200cee37aa0f6990da3d" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "47a5c4cbcdc5fda84d74a947", +"None": "47a5c4cbcdc5fda84d74a947", +"bigquery": "47a5c4cbcdc5fda84d74a947", +"databricks": "47a5c4cbcdc5fda84d74a947", +"duckdb": "47a5c4cbcdc5fda84d74a947", +"generic": "47a5c4cbcdc5fda84d74a947", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "47a5c4cbcdc5fda84d74a947", +"postgres": "47a5c4cbcdc5fda84d74a947", +"postgresql": "47a5c4cbcdc5fda84d74a947", +"snowflake": "47a5c4cbcdc5fda84d74a947", +"tsql": "47a5c4cbcdc5fda84d74a947" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "792f7d3389f076f50c713946", +"None": "792f7d3389f076f50c713946", +"bigquery": "792f7d3389f076f50c713946", +"databricks": "4fb572024031cb7631777be1", +"duckdb": "792f7d3389f076f50c713946", +"generic": "792f7d3389f076f50c713946", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "792f7d3389f076f50c713946", +"postgres": "792f7d3389f076f50c713946", +"postgresql": "792f7d3389f076f50c713946", +"snowflake": "792f7d3389f076f50c713946", +"tsql": "792f7d3389f076f50c713946" +} +], +[ +"type", +{ +"DuckDB": "aae2b1fe6f2d9807ecde412b", +"None": "aae2b1fe6f2d9807ecde412b", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "aae2b1fe6f2d9807ecde412b", +"duckdb": "aae2b1fe6f2d9807ecde412b", +"generic": "aae2b1fe6f2d9807ecde412b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aae2b1fe6f2d9807ecde412b", +"postgres": "aae2b1fe6f2d9807ecde412b", +"postgresql": "aae2b1fe6f2d9807ecde412b", +"snowflake": "aae2b1fe6f2d9807ecde412b", +"tsql": "aae2b1fe6f2d9807ecde412b" +} +], +[ +"type", +{ +"DuckDB": "16adcdd909f51e3f33d7cdde", +"None": "1f3277ab2fff19e327e21fe0", +"bigquery": "1f3277ab2fff19e327e21fe0", +"databricks": "1f3277ab2fff19e327e21fe0", +"duckdb": "16adcdd909f51e3f33d7cdde", +"generic": "1f3277ab2fff19e327e21fe0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "de3f085b785f38a4bda19034", +"postgresql": "de3f085b785f38a4bda19034", +"snowflake": "42ed29994060215ec30494f1", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "690b44350cdb6852c9bc71a3", +"None": "690b44350cdb6852c9bc71a3", +"bigquery": "690b44350cdb6852c9bc71a3", +"databricks": "690b44350cdb6852c9bc71a3", +"duckdb": "690b44350cdb6852c9bc71a3", +"generic": "690b44350cdb6852c9bc71a3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "690b44350cdb6852c9bc71a3", +"postgres": "690b44350cdb6852c9bc71a3", +"postgresql": "690b44350cdb6852c9bc71a3", +"snowflake": "690b44350cdb6852c9bc71a3", +"tsql": "690b44350cdb6852c9bc71a3" +} +], +[ +"type", +{ +"DuckDB": "3a7ffeb8f81df6ccd57f3689", +"None": "3a7ffeb8f81df6ccd57f3689", +"bigquery": "3a7ffeb8f81df6ccd57f3689", +"databricks": "3a7ffeb8f81df6ccd57f3689", +"duckdb": "3a7ffeb8f81df6ccd57f3689", +"generic": "3a7ffeb8f81df6ccd57f3689", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3a7ffeb8f81df6ccd57f3689", +"postgres": "3a7ffeb8f81df6ccd57f3689", +"postgresql": "3a7ffeb8f81df6ccd57f3689", +"snowflake": "3a7ffeb8f81df6ccd57f3689", +"tsql": "3a7ffeb8f81df6ccd57f3689" +} +], +[ +"type", +{ +"DuckDB": "c752fd9fe2ad3b86b8b899da", +"None": "c752fd9fe2ad3b86b8b899da", +"bigquery": "c752fd9fe2ad3b86b8b899da", +"databricks": "c752fd9fe2ad3b86b8b899da", +"duckdb": "c752fd9fe2ad3b86b8b899da", +"generic": "c752fd9fe2ad3b86b8b899da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c752fd9fe2ad3b86b8b899da", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c752fd9fe2ad3b86b8b899da" +} +], +[ +"type", +{ +"DuckDB": "01d51e9454e607e1bab153ef", +"None": "01d51e9454e607e1bab153ef", +"bigquery": "01d51e9454e607e1bab153ef", +"databricks": "01d51e9454e607e1bab153ef", +"duckdb": "01d51e9454e607e1bab153ef", +"generic": "01d51e9454e607e1bab153ef", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "01d51e9454e607e1bab153ef", +"postgres": "01d51e9454e607e1bab153ef", +"postgresql": "01d51e9454e607e1bab153ef", +"snowflake": "01d51e9454e607e1bab153ef", +"tsql": "01d51e9454e607e1bab153ef" +} +], +[ +"type", +{ +"DuckDB": "802f1c1fcb97ddd5679172d2", +"None": "802f1c1fcb97ddd5679172d2", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "802f1c1fcb97ddd5679172d2", +"duckdb": "802f1c1fcb97ddd5679172d2", +"generic": "802f1c1fcb97ddd5679172d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "802f1c1fcb97ddd5679172d2", +"postgres": "802f1c1fcb97ddd5679172d2", +"postgresql": "802f1c1fcb97ddd5679172d2", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "08784560ad78e14a02cc066f" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "fe0bedb7dda0e9247a51fdee", +"None": "fe0bedb7dda0e9247a51fdee", +"bigquery": "fe0bedb7dda0e9247a51fdee", +"databricks": "fe0bedb7dda0e9247a51fdee", +"duckdb": "fe0bedb7dda0e9247a51fdee", +"generic": "fe0bedb7dda0e9247a51fdee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe0bedb7dda0e9247a51fdee", +"postgres": "fe0bedb7dda0e9247a51fdee", +"postgresql": "fe0bedb7dda0e9247a51fdee", +"snowflake": "fe0bedb7dda0e9247a51fdee", +"tsql": "fe0bedb7dda0e9247a51fdee" +} +], +[ +"type", +{ +"DuckDB": "e1250582e271934cbcb2a788", +"None": "e1250582e271934cbcb2a788", +"bigquery": "e1250582e271934cbcb2a788", +"databricks": "e1250582e271934cbcb2a788", +"duckdb": "e1250582e271934cbcb2a788", +"generic": "e1250582e271934cbcb2a788", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1250582e271934cbcb2a788", +"postgres": "e1250582e271934cbcb2a788", +"postgresql": "e1250582e271934cbcb2a788", +"snowflake": "e1250582e271934cbcb2a788", +"tsql": "e1250582e271934cbcb2a788" +} +], +[ +"type", +{ +"DuckDB": "dbf9ebbc7016f5ac5df2817e", +"None": "dbf9ebbc7016f5ac5df2817e", +"bigquery": "dbf9ebbc7016f5ac5df2817e", +"databricks": "dbf9ebbc7016f5ac5df2817e", +"duckdb": "dbf9ebbc7016f5ac5df2817e", +"generic": "dbf9ebbc7016f5ac5df2817e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dbf9ebbc7016f5ac5df2817e", +"postgres": "dbf9ebbc7016f5ac5df2817e", +"postgresql": "dbf9ebbc7016f5ac5df2817e", +"snowflake": "dbf9ebbc7016f5ac5df2817e", +"tsql": "dbf9ebbc7016f5ac5df2817e" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "72bd5e38b32893397eb51579", +"None": "730d7cf7510ba0934828d971", +"bigquery": "730d7cf7510ba0934828d971", +"databricks": "730d7cf7510ba0934828d971", +"duckdb": "72bd5e38b32893397eb51579", +"generic": "730d7cf7510ba0934828d971", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1bdf5908ca249b2331a0427c", +"postgres": "1bdf5908ca249b2331a0427c", +"postgresql": "1bdf5908ca249b2331a0427c", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "1bdf5908ca249b2331a0427c" +} +], +[ +"type", +{ +"DuckDB": "b0791f9f270e764eeeb6fb8e", +"None": "c7c38fdeaeb7858a44976f1d", +"bigquery": "cfb4a49a57197b0df1b2e905", +"databricks": "6bd27be6a47ef34ec1defbad", +"duckdb": "b0791f9f270e764eeeb6fb8e", +"generic": "c7c38fdeaeb7858a44976f1d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c7c38fdeaeb7858a44976f1d", +"postgres": "4d7604d41e3585ec187dec82", +"postgresql": "4d7604d41e3585ec187dec82", +"snowflake": "c7c38fdeaeb7858a44976f1d", +"tsql": "b0791f9f270e764eeeb6fb8e" +} +], +[ +"type", +{ +"DuckDB": "0b9d9879e289ea9a6a3e3e70", +"None": "0b9d9879e289ea9a6a3e3e70", +"bigquery": "0b9d9879e289ea9a6a3e3e70", +"databricks": "0b9d9879e289ea9a6a3e3e70", +"duckdb": "0b9d9879e289ea9a6a3e3e70", +"generic": "0b9d9879e289ea9a6a3e3e70", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b9d9879e289ea9a6a3e3e70", +"postgres": "0b9d9879e289ea9a6a3e3e70", +"postgresql": "0b9d9879e289ea9a6a3e3e70", +"snowflake": "0b9d9879e289ea9a6a3e3e70", +"tsql": "0b9d9879e289ea9a6a3e3e70" +} +], +[ +"type", +{ +"DuckDB": "ba0da7ef018b990fcc83cba1", +"None": "ba0da7ef018b990fcc83cba1", +"bigquery": "ba0da7ef018b990fcc83cba1", +"databricks": "ba0da7ef018b990fcc83cba1", +"duckdb": "ba0da7ef018b990fcc83cba1", +"generic": "ba0da7ef018b990fcc83cba1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ba0da7ef018b990fcc83cba1", +"postgres": "ba0da7ef018b990fcc83cba1", +"postgresql": "ba0da7ef018b990fcc83cba1", +"snowflake": "ba0da7ef018b990fcc83cba1", +"tsql": "ba0da7ef018b990fcc83cba1" +} +], +[ +"type", +{ +"DuckDB": "51b0e597909858f7f7aa4571", +"None": "51b0e597909858f7f7aa4571", +"bigquery": "deb9d53b04b7825e460d826a", +"databricks": "846f4ee64c34aab0cdabb9a0", +"duckdb": "51b0e597909858f7f7aa4571", +"generic": "51b0e597909858f7f7aa4571", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "51b0e597909858f7f7aa4571", +"postgres": "51b0e597909858f7f7aa4571", +"postgresql": "51b0e597909858f7f7aa4571", +"snowflake": "50d14e3a5691e6cb8689aa29", +"tsql": "51b0e597909858f7f7aa4571" +} +], +[ +"type", +{ +"DuckDB": "8698d724534891869075175e", +"None": "0017a88bb60ec3403c786ed8", +"bigquery": "2795066c9a71fa3e3780c73b", +"databricks": "0017a88bb60ec3403c786ed8", +"duckdb": "8698d724534891869075175e", +"generic": "0017a88bb60ec3403c786ed8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8698d724534891869075175e", +"postgresql": "8698d724534891869075175e", +"snowflake": "b3b752c645651b849e23d39a", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "881106a4a0d2de0ff7c56373", +"None": "881106a4a0d2de0ff7c56373", +"bigquery": "881106a4a0d2de0ff7c56373", +"databricks": "881106a4a0d2de0ff7c56373", +"duckdb": "881106a4a0d2de0ff7c56373", +"generic": "881106a4a0d2de0ff7c56373", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "881106a4a0d2de0ff7c56373", +"postgres": "881106a4a0d2de0ff7c56373", +"postgresql": "881106a4a0d2de0ff7c56373", +"snowflake": "881106a4a0d2de0ff7c56373", +"tsql": "881106a4a0d2de0ff7c56373" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "013a8446f043affffcf194f8", +"None": "013a8446f043affffcf194f8", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "013a8446f043affffcf194f8", +"duckdb": "013a8446f043affffcf194f8", +"generic": "013a8446f043affffcf194f8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "013a8446f043affffcf194f8", +"postgres": "013a8446f043affffcf194f8", +"postgresql": "013a8446f043affffcf194f8", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "013a8446f043affffcf194f8" +} +], +[ +"type", +{ +"DuckDB": "804fec6876d0b43935790142", +"None": "804fec6876d0b43935790142", +"bigquery": "804fec6876d0b43935790142", +"databricks": "804fec6876d0b43935790142", +"duckdb": "804fec6876d0b43935790142", +"generic": "804fec6876d0b43935790142", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "804fec6876d0b43935790142", +"postgres": "804fec6876d0b43935790142", +"postgresql": "804fec6876d0b43935790142", +"snowflake": "804fec6876d0b43935790142", +"tsql": "804fec6876d0b43935790142" +} +], +[ +"type", +{ +"DuckDB": "0dcd6a19b7dc6f6b6d4a5006", +"None": "0dcd6a19b7dc6f6b6d4a5006", +"bigquery": "0dcd6a19b7dc6f6b6d4a5006", +"databricks": "0dcd6a19b7dc6f6b6d4a5006", +"duckdb": "0dcd6a19b7dc6f6b6d4a5006", +"generic": "0dcd6a19b7dc6f6b6d4a5006", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dcd6a19b7dc6f6b6d4a5006", +"postgres": "0dcd6a19b7dc6f6b6d4a5006", +"postgresql": "0dcd6a19b7dc6f6b6d4a5006", +"snowflake": "0dcd6a19b7dc6f6b6d4a5006", +"tsql": "0dcd6a19b7dc6f6b6d4a5006" +} +], +[ +"type", +{ +"DuckDB": "9d6807d797be08f8376a5a19", +"None": "9d6807d797be08f8376a5a19", +"bigquery": "9d6807d797be08f8376a5a19", +"databricks": "9d6807d797be08f8376a5a19", +"duckdb": "9d6807d797be08f8376a5a19", +"generic": "9d6807d797be08f8376a5a19", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9d6807d797be08f8376a5a19", +"postgres": "9d6807d797be08f8376a5a19", +"postgresql": "9d6807d797be08f8376a5a19", +"snowflake": "9d6807d797be08f8376a5a19", +"tsql": "9d6807d797be08f8376a5a19" +} +], +[ +"type", +{ +"DuckDB": "3915f3f73328f0838497b2d5", +"None": "3915f3f73328f0838497b2d5", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "3915f3f73328f0838497b2d5", +"duckdb": "3915f3f73328f0838497b2d5", +"generic": "3915f3f73328f0838497b2d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3915f3f73328f0838497b2d5", +"postgres": "3915f3f73328f0838497b2d5", +"postgresql": "3915f3f73328f0838497b2d5", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3915f3f73328f0838497b2d5" +} +], +[ +"type", +{ +"DuckDB": "0dcd6a19b7dc6f6b6d4a5006", +"None": "0dcd6a19b7dc6f6b6d4a5006", +"bigquery": "0dcd6a19b7dc6f6b6d4a5006", +"databricks": "0dcd6a19b7dc6f6b6d4a5006", +"duckdb": "0dcd6a19b7dc6f6b6d4a5006", +"generic": "0dcd6a19b7dc6f6b6d4a5006", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dcd6a19b7dc6f6b6d4a5006", +"postgres": "0dcd6a19b7dc6f6b6d4a5006", +"postgresql": "0dcd6a19b7dc6f6b6d4a5006", +"snowflake": "0dcd6a19b7dc6f6b6d4a5006", +"tsql": "0dcd6a19b7dc6f6b6d4a5006" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "d03c5e5797a8cbc0c6a81112", +"None": "d03c5e5797a8cbc0c6a81112", +"bigquery": "d03c5e5797a8cbc0c6a81112", +"databricks": "d03c5e5797a8cbc0c6a81112", +"duckdb": "d03c5e5797a8cbc0c6a81112", +"generic": "d03c5e5797a8cbc0c6a81112", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d03c5e5797a8cbc0c6a81112", +"postgres": "d03c5e5797a8cbc0c6a81112", +"postgresql": "d03c5e5797a8cbc0c6a81112", +"snowflake": "d03c5e5797a8cbc0c6a81112", +"tsql": "d03c5e5797a8cbc0c6a81112" +} +], +[ +"type", +{ +"DuckDB": "6cffd1617edd6454241ee732", +"None": "6cffd1617edd6454241ee732", +"bigquery": "6cffd1617edd6454241ee732", +"databricks": "6cffd1617edd6454241ee732", +"duckdb": "6cffd1617edd6454241ee732", +"generic": "6cffd1617edd6454241ee732", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6cffd1617edd6454241ee732", +"postgres": "6cffd1617edd6454241ee732", +"postgresql": "6cffd1617edd6454241ee732", +"snowflake": "6cffd1617edd6454241ee732", +"tsql": "6cffd1617edd6454241ee732" +} +], +[ +"type", +{ +"DuckDB": "815698ef0c2515c76f250086", +"None": "98d56a64e9a7ee905a58c95c", +"bigquery": "98d56a64e9a7ee905a58c95c", +"databricks": "98d56a64e9a7ee905a58c95c", +"duckdb": "815698ef0c2515c76f250086", +"generic": "98d56a64e9a7ee905a58c95c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "815698ef0c2515c76f250086", +"postgresql": "815698ef0c2515c76f250086", +"snowflake": "822641f83fbdcddae47b409f", +"tsql": "b34cea19c30a73dc21f7a04c" +} +], +[ +"type", +{ +"DuckDB": "1275b5b0c52f69cf592c004b", +"None": "1275b5b0c52f69cf592c004b", +"bigquery": "1275b5b0c52f69cf592c004b", +"databricks": "1275b5b0c52f69cf592c004b", +"duckdb": "1275b5b0c52f69cf592c004b", +"generic": "1275b5b0c52f69cf592c004b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1275b5b0c52f69cf592c004b", +"postgres": "1275b5b0c52f69cf592c004b", +"postgresql": "1275b5b0c52f69cf592c004b", +"snowflake": "1275b5b0c52f69cf592c004b", +"tsql": "1275b5b0c52f69cf592c004b" +} +], +[ +"type", +{ +"DuckDB": "80d6c9c5f502ef25b9434309", +"None": "80d6c9c5f502ef25b9434309", +"bigquery": "80d6c9c5f502ef25b9434309", +"databricks": "80d6c9c5f502ef25b9434309", +"duckdb": "80d6c9c5f502ef25b9434309", +"generic": "80d6c9c5f502ef25b9434309", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "80d6c9c5f502ef25b9434309", +"postgres": "80d6c9c5f502ef25b9434309", +"postgresql": "80d6c9c5f502ef25b9434309", +"snowflake": "80d6c9c5f502ef25b9434309", +"tsql": "80d6c9c5f502ef25b9434309" +} +], +[ +"type", +{ +"DuckDB": "42f6a9fe8a8cc486c2c77ae1", +"None": "42f6a9fe8a8cc486c2c77ae1", +"bigquery": "42f6a9fe8a8cc486c2c77ae1", +"databricks": "42f6a9fe8a8cc486c2c77ae1", +"duckdb": "42f6a9fe8a8cc486c2c77ae1", +"generic": "42f6a9fe8a8cc486c2c77ae1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42f6a9fe8a8cc486c2c77ae1", +"postgres": "42f6a9fe8a8cc486c2c77ae1", +"postgresql": "42f6a9fe8a8cc486c2c77ae1", +"snowflake": "42f6a9fe8a8cc486c2c77ae1", +"tsql": "42f6a9fe8a8cc486c2c77ae1" +} +], +[ +"type", +{ +"DuckDB": "7894730ae1c06f4ea3920947", +"None": "7894730ae1c06f4ea3920947", +"bigquery": "7894730ae1c06f4ea3920947", +"databricks": "7894730ae1c06f4ea3920947", +"duckdb": "7894730ae1c06f4ea3920947", +"generic": "7894730ae1c06f4ea3920947", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7894730ae1c06f4ea3920947", +"postgres": "7894730ae1c06f4ea3920947", +"postgresql": "7894730ae1c06f4ea3920947", +"snowflake": "7894730ae1c06f4ea3920947", +"tsql": "7894730ae1c06f4ea3920947" +} +], +[ +"type", +{ +"DuckDB": "f0c468b3e6d6df98b4358aff", +"None": "752ae7fc618de4a182e18bd3", +"bigquery": "752ae7fc618de4a182e18bd3", +"databricks": "aa5bc52ba6ea9e0a428084da", +"duckdb": "f0c468b3e6d6df98b4358aff", +"generic": "752ae7fc618de4a182e18bd3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "f0c468b3e6d6df98b4358aff", +"postgresql": "f0c468b3e6d6df98b4358aff", +"snowflake": "6dc518f5329c32ce13bfa506", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "9829fa9011a92bb968bb5996", +"None": "9829fa9011a92bb968bb5996", +"bigquery": "9829fa9011a92bb968bb5996", +"databricks": "9829fa9011a92bb968bb5996", +"duckdb": "9829fa9011a92bb968bb5996", +"generic": "9829fa9011a92bb968bb5996", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9829fa9011a92bb968bb5996", +"postgres": "9829fa9011a92bb968bb5996", +"postgresql": "9829fa9011a92bb968bb5996", +"snowflake": "9829fa9011a92bb968bb5996", +"tsql": "9829fa9011a92bb968bb5996" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "0ca1be8f7d61ca627e620450", +"None": "0ca1be8f7d61ca627e620450", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "0ca1be8f7d61ca627e620450", +"duckdb": "0ca1be8f7d61ca627e620450", +"generic": "0ca1be8f7d61ca627e620450", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0ca1be8f7d61ca627e620450", +"postgres": "0ca1be8f7d61ca627e620450", +"postgresql": "0ca1be8f7d61ca627e620450", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "0ca1be8f7d61ca627e620450" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "8993aba016a0adde404608e3", +"None": "8993aba016a0adde404608e3", +"bigquery": "8993aba016a0adde404608e3", +"databricks": "8993aba016a0adde404608e3", +"duckdb": "8993aba016a0adde404608e3", +"generic": "8993aba016a0adde404608e3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8993aba016a0adde404608e3", +"postgres": "8993aba016a0adde404608e3", +"postgresql": "8993aba016a0adde404608e3", +"snowflake": "8993aba016a0adde404608e3", +"tsql": "8993aba016a0adde404608e3" +} +], +[ +"type", +{ +"DuckDB": "837168a49b84e7ed918ea148", +"None": "837168a49b84e7ed918ea148", +"bigquery": "837168a49b84e7ed918ea148", +"databricks": "837168a49b84e7ed918ea148", +"duckdb": "837168a49b84e7ed918ea148", +"generic": "837168a49b84e7ed918ea148", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "837168a49b84e7ed918ea148", +"postgres": "837168a49b84e7ed918ea148", +"postgresql": "837168a49b84e7ed918ea148", +"snowflake": "837168a49b84e7ed918ea148", +"tsql": "837168a49b84e7ed918ea148" +} +], +[ +"type", +{ +"DuckDB": "3915f3f73328f0838497b2d5", +"None": "3915f3f73328f0838497b2d5", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "3915f3f73328f0838497b2d5", +"duckdb": "3915f3f73328f0838497b2d5", +"generic": "3915f3f73328f0838497b2d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3915f3f73328f0838497b2d5", +"postgres": "3915f3f73328f0838497b2d5", +"postgresql": "3915f3f73328f0838497b2d5", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3915f3f73328f0838497b2d5" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "624eb95d280fa0653ec73f7f", +"None": "624eb95d280fa0653ec73f7f", +"bigquery": "624eb95d280fa0653ec73f7f", +"databricks": "624eb95d280fa0653ec73f7f", +"duckdb": "624eb95d280fa0653ec73f7f", +"generic": "624eb95d280fa0653ec73f7f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "624eb95d280fa0653ec73f7f", +"postgres": "624eb95d280fa0653ec73f7f", +"postgresql": "624eb95d280fa0653ec73f7f", +"snowflake": "624eb95d280fa0653ec73f7f", +"tsql": "624eb95d280fa0653ec73f7f" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "bce60580ef3a1dc4ce569fff", +"None": "bce60580ef3a1dc4ce569fff", +"bigquery": "bce60580ef3a1dc4ce569fff", +"databricks": "bce60580ef3a1dc4ce569fff", +"duckdb": "bce60580ef3a1dc4ce569fff", +"generic": "bce60580ef3a1dc4ce569fff", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bce60580ef3a1dc4ce569fff", +"postgres": "bce60580ef3a1dc4ce569fff", +"postgresql": "bce60580ef3a1dc4ce569fff", +"snowflake": "bce60580ef3a1dc4ce569fff", +"tsql": "bce60580ef3a1dc4ce569fff" +} +], +[ +"type", +{ +"DuckDB": "a104f9b34b3d9d798095a6b4", +"None": "a104f9b34b3d9d798095a6b4", +"bigquery": "a104f9b34b3d9d798095a6b4", +"databricks": "a104f9b34b3d9d798095a6b4", +"duckdb": "a104f9b34b3d9d798095a6b4", +"generic": "a104f9b34b3d9d798095a6b4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a104f9b34b3d9d798095a6b4", +"postgres": "a104f9b34b3d9d798095a6b4", +"postgresql": "a104f9b34b3d9d798095a6b4", +"snowflake": "a104f9b34b3d9d798095a6b4", +"tsql": "a104f9b34b3d9d798095a6b4" +} +], +[ +"type", +{ +"DuckDB": "858855737713d0cff353f8ac", +"None": "858855737713d0cff353f8ac", +"bigquery": "858855737713d0cff353f8ac", +"databricks": "858855737713d0cff353f8ac", +"duckdb": "858855737713d0cff353f8ac", +"generic": "858855737713d0cff353f8ac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "858855737713d0cff353f8ac", +"postgres": "858855737713d0cff353f8ac", +"postgresql": "858855737713d0cff353f8ac", +"snowflake": "858855737713d0cff353f8ac", +"tsql": "858855737713d0cff353f8ac" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "4ce8ae479c93f9ff48189fc3", +"None": "4ce8ae479c93f9ff48189fc3", +"bigquery": "4ce8ae479c93f9ff48189fc3", +"databricks": "4ce8ae479c93f9ff48189fc3", +"duckdb": "4ce8ae479c93f9ff48189fc3", +"generic": "4ce8ae479c93f9ff48189fc3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4ce8ae479c93f9ff48189fc3", +"postgres": "4ce8ae479c93f9ff48189fc3", +"postgresql": "4ce8ae479c93f9ff48189fc3", +"snowflake": "4ce8ae479c93f9ff48189fc3", +"tsql": "4ce8ae479c93f9ff48189fc3" +} +], +[ +"type", +{ +"DuckDB": "fb5907c56dd752ac5f1dbd95", +"None": "fb5907c56dd752ac5f1dbd95", +"bigquery": "fb5907c56dd752ac5f1dbd95", +"databricks": "fb5907c56dd752ac5f1dbd95", +"duckdb": "fb5907c56dd752ac5f1dbd95", +"generic": "fb5907c56dd752ac5f1dbd95", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fb5907c56dd752ac5f1dbd95", +"postgres": "fb5907c56dd752ac5f1dbd95", +"postgresql": "fb5907c56dd752ac5f1dbd95", +"snowflake": "fb5907c56dd752ac5f1dbd95", +"tsql": "fb5907c56dd752ac5f1dbd95" +} +], +[ +"type", +{ +"DuckDB": "aab5b077409069695e7d847d", +"None": "aab5b077409069695e7d847d", +"bigquery": "aab5b077409069695e7d847d", +"databricks": "aab5b077409069695e7d847d", +"duckdb": "aab5b077409069695e7d847d", +"generic": "aab5b077409069695e7d847d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab5b077409069695e7d847d", +"postgres": "aab5b077409069695e7d847d", +"postgresql": "aab5b077409069695e7d847d", +"snowflake": "aab5b077409069695e7d847d", +"tsql": "aab5b077409069695e7d847d" +} +], +[ +"type", +{ +"DuckDB": "638ca9212b62ada31a448c62", +"None": "1fec266134381c760a254d16", +"bigquery": "1fec266134381c760a254d16", +"databricks": "1fec266134381c760a254d16", +"duckdb": "638ca9212b62ada31a448c62", +"generic": "1fec266134381c760a254d16", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fec266134381c760a254d16", +"postgres": "1fec266134381c760a254d16", +"postgresql": "1fec266134381c760a254d16", +"snowflake": "1fec266134381c760a254d16", +"tsql": "1fec266134381c760a254d16" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "b1b23029bedaa16e2a6f8516", +"None": "b1b23029bedaa16e2a6f8516", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "b1b23029bedaa16e2a6f8516", +"duckdb": "b1b23029bedaa16e2a6f8516", +"generic": "b1b23029bedaa16e2a6f8516", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b1b23029bedaa16e2a6f8516", +"postgres": "b1b23029bedaa16e2a6f8516", +"postgresql": "b1b23029bedaa16e2a6f8516", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "b1b23029bedaa16e2a6f8516" +} +], +[ +"type", +{ +"DuckDB": "111af35848fe500ec2ae54b2", +"None": "111af35848fe500ec2ae54b2", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "111af35848fe500ec2ae54b2", +"generic": "111af35848fe500ec2ae54b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e3bc1b8bad22d8104417b00f", +"postgres": "111af35848fe500ec2ae54b2", +"postgresql": "111af35848fe500ec2ae54b2", +"snowflake": "e3bc1b8bad22d8104417b00f", +"tsql": "111af35848fe500ec2ae54b2" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "47d74d1b45fedd02ec465220", +"None": "47d74d1b45fedd02ec465220", +"bigquery": "47d74d1b45fedd02ec465220", +"databricks": "47d74d1b45fedd02ec465220", +"duckdb": "47d74d1b45fedd02ec465220", +"generic": "47d74d1b45fedd02ec465220", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "47d74d1b45fedd02ec465220", +"postgres": "47d74d1b45fedd02ec465220", +"postgresql": "47d74d1b45fedd02ec465220", +"snowflake": "47d74d1b45fedd02ec465220", +"tsql": "47d74d1b45fedd02ec465220" +} +], +[ +"type", +{ +"DuckDB": "60a23b7dcae6c79f4de83068", +"None": "60a23b7dcae6c79f4de83068", +"bigquery": "60a23b7dcae6c79f4de83068", +"databricks": "60a23b7dcae6c79f4de83068", +"duckdb": "60a23b7dcae6c79f4de83068", +"generic": "60a23b7dcae6c79f4de83068", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60a23b7dcae6c79f4de83068", +"postgres": "60a23b7dcae6c79f4de83068", +"postgresql": "60a23b7dcae6c79f4de83068", +"snowflake": "60a23b7dcae6c79f4de83068", +"tsql": "60a23b7dcae6c79f4de83068" +} +], +[ +"type", +{ +"DuckDB": "1879bc874141d9160106e020", +"None": "c513dcc8ed8dfd8935374a47", +"bigquery": "c513dcc8ed8dfd8935374a47", +"databricks": "c513dcc8ed8dfd8935374a47", +"duckdb": "1879bc874141d9160106e020", +"generic": "c513dcc8ed8dfd8935374a47", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c513dcc8ed8dfd8935374a47", +"postgres": "c513dcc8ed8dfd8935374a47", +"postgresql": "c513dcc8ed8dfd8935374a47", +"snowflake": "c513dcc8ed8dfd8935374a47", +"tsql": "c513dcc8ed8dfd8935374a47" +} +], +[ +"type", +{ +"DuckDB": "2705f19942a4194a72576911", +"None": "2705f19942a4194a72576911", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "2705f19942a4194a72576911", +"duckdb": "2705f19942a4194a72576911", +"generic": "2705f19942a4194a72576911", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2705f19942a4194a72576911", +"postgres": "2705f19942a4194a72576911", +"postgresql": "2705f19942a4194a72576911", +"snowflake": "2705f19942a4194a72576911", +"tsql": "2705f19942a4194a72576911" +} +], +[ +"type", +{ +"DuckDB": "9b1399d4f1923837359745f8", +"None": "9b1399d4f1923837359745f8", +"bigquery": "9b1399d4f1923837359745f8", +"databricks": "9b1399d4f1923837359745f8", +"duckdb": "9b1399d4f1923837359745f8", +"generic": "9b1399d4f1923837359745f8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9b1399d4f1923837359745f8", +"postgres": "9b1399d4f1923837359745f8", +"postgresql": "9b1399d4f1923837359745f8", +"snowflake": "9b1399d4f1923837359745f8", +"tsql": "9b1399d4f1923837359745f8" +} +], +[ +"type", +{ +"DuckDB": "c43574432afc90bcba6944b2", +"None": "7040afbb3a088baf55e328b2", +"bigquery": "ba611057434e43344935471b", +"databricks": "ba611057434e43344935471b", +"duckdb": "c43574432afc90bcba6944b2", +"generic": "7040afbb3a088baf55e328b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "1b3243409566f14d81c11fff", +"postgresql": "1b3243409566f14d81c11fff", +"snowflake": "06a9af03923de91e5e27860a", +"tsql": "3866c29c7e0df841d464b98c" +} +], +[ +"type", +{ +"DuckDB": "d229e289e63364bf0d56e920", +"None": "28c3447161aa1c0a550659b8", +"bigquery": "28c3447161aa1c0a550659b8", +"databricks": "b8e50cd0369cf0810e7054be", +"duckdb": "d229e289e63364bf0d56e920", +"generic": "28c3447161aa1c0a550659b8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28c3447161aa1c0a550659b8", +"postgres": "28c3447161aa1c0a550659b8", +"postgresql": "28c3447161aa1c0a550659b8", +"snowflake": "d90e8915a295b2cd5e690bd1", +"tsql": "28c3447161aa1c0a550659b8" +} +], +[ +"type", +{ +"DuckDB": "42d4a23d9a52e79d0c3458d3", +"None": "a3acd7ba35e81a800f967bd3", +"bigquery": "3c0719ca5c83e0a20d0b0b42", +"databricks": "a3acd7ba35e81a800f967bd3", +"duckdb": "42d4a23d9a52e79d0c3458d3", +"generic": "a3acd7ba35e81a800f967bd3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a3acd7ba35e81a800f967bd3", +"postgres": "a3acd7ba35e81a800f967bd3", +"postgresql": "a3acd7ba35e81a800f967bd3", +"snowflake": "a3acd7ba35e81a800f967bd3", +"tsql": "a3acd7ba35e81a800f967bd3" +} +], +[ +"type", +{ +"DuckDB": "c6106f30f23f392e17eadef4", +"None": "c6106f30f23f392e17eadef4", +"bigquery": "c6106f30f23f392e17eadef4", +"databricks": "c6106f30f23f392e17eadef4", +"duckdb": "c6106f30f23f392e17eadef4", +"generic": "c6106f30f23f392e17eadef4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6106f30f23f392e17eadef4", +"postgres": "c6106f30f23f392e17eadef4", +"postgresql": "c6106f30f23f392e17eadef4", +"snowflake": "c6106f30f23f392e17eadef4", +"tsql": "c6106f30f23f392e17eadef4" +} +], +[ +"type", +{ +"DuckDB": "804fec6876d0b43935790142", +"None": "804fec6876d0b43935790142", +"bigquery": "804fec6876d0b43935790142", +"databricks": "804fec6876d0b43935790142", +"duckdb": "804fec6876d0b43935790142", +"generic": "804fec6876d0b43935790142", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "804fec6876d0b43935790142", +"postgres": "804fec6876d0b43935790142", +"postgresql": "804fec6876d0b43935790142", +"snowflake": "804fec6876d0b43935790142", +"tsql": "804fec6876d0b43935790142" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "194c385aa00f52f45426748f", +"None": "7b87db889221e05e13ec3845", +"bigquery": "7b87db889221e05e13ec3845", +"databricks": "7b87db889221e05e13ec3845", +"duckdb": "194c385aa00f52f45426748f", +"generic": "7b87db889221e05e13ec3845", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7b87db889221e05e13ec3845", +"postgres": "7b87db889221e05e13ec3845", +"postgresql": "7b87db889221e05e13ec3845", +"snowflake": "7b87db889221e05e13ec3845", +"tsql": "7b87db889221e05e13ec3845" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "c69a366e715a81bca598d475", +"None": "c69a366e715a81bca598d475", +"bigquery": "b5e374ae8299a92b732a3c43", +"databricks": "eb72e9a8e16bab9ee01146ee", +"duckdb": "c69a366e715a81bca598d475", +"generic": "c69a366e715a81bca598d475", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c69a366e715a81bca598d475", +"postgres": "c69a366e715a81bca598d475", +"postgresql": "c69a366e715a81bca598d475", +"snowflake": "f41d94b0bed9910074641da6", +"tsql": "c69a366e715a81bca598d475" +} +], +[ +"type", +{ +"DuckDB": "558da38ec4d9d5d6c72f60cf", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "558da38ec4d9d5d6c72f60cf", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "b210f8eb26c37b422f786c6b", +"None": "b210f8eb26c37b422f786c6b", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "b210f8eb26c37b422f786c6b", +"duckdb": "b210f8eb26c37b422f786c6b", +"generic": "b210f8eb26c37b422f786c6b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b210f8eb26c37b422f786c6b", +"postgres": "b210f8eb26c37b422f786c6b", +"postgresql": "b210f8eb26c37b422f786c6b", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "b210f8eb26c37b422f786c6b" +} +], +[ +"type", +{ +"DuckDB": "279ea7f6f6a3910de04b795b", +"None": "279ea7f6f6a3910de04b795b", +"bigquery": "279ea7f6f6a3910de04b795b", +"databricks": "279ea7f6f6a3910de04b795b", +"duckdb": "279ea7f6f6a3910de04b795b", +"generic": "279ea7f6f6a3910de04b795b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "279ea7f6f6a3910de04b795b", +"postgres": "279ea7f6f6a3910de04b795b", +"postgresql": "279ea7f6f6a3910de04b795b", +"snowflake": "279ea7f6f6a3910de04b795b", +"tsql": "279ea7f6f6a3910de04b795b" +} +], +[ +"type", +{ +"DuckDB": "92e8caae709d951bb8ed59cb", +"None": "92e8caae709d951bb8ed59cb", +"bigquery": "92e8caae709d951bb8ed59cb", +"databricks": "1758ac00dad1bae84521b3fb", +"duckdb": "92e8caae709d951bb8ed59cb", +"generic": "92e8caae709d951bb8ed59cb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1758ac00dad1bae84521b3fb", +"postgres": "92e8caae709d951bb8ed59cb", +"postgresql": "92e8caae709d951bb8ed59cb", +"snowflake": "92e8caae709d951bb8ed59cb", +"tsql": "92e8caae709d951bb8ed59cb" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "a686bb6db17ab079cfc79224", +"None": "a686bb6db17ab079cfc79224", +"bigquery": "a686bb6db17ab079cfc79224", +"databricks": "a686bb6db17ab079cfc79224", +"duckdb": "a686bb6db17ab079cfc79224", +"generic": "a686bb6db17ab079cfc79224", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a686bb6db17ab079cfc79224", +"postgres": "a686bb6db17ab079cfc79224", +"postgresql": "a686bb6db17ab079cfc79224", +"snowflake": "1096c3c8687c8e8eb538ff74", +"tsql": "a686bb6db17ab079cfc79224" +} +], +[ +"type", +{ +"DuckDB": "56f06885daf94b4fcc0a624b", +"None": "56f06885daf94b4fcc0a624b", +"bigquery": "56f06885daf94b4fcc0a624b", +"databricks": "56f06885daf94b4fcc0a624b", +"duckdb": "56f06885daf94b4fcc0a624b", +"generic": "56f06885daf94b4fcc0a624b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "56f06885daf94b4fcc0a624b", +"postgres": "56f06885daf94b4fcc0a624b", +"postgresql": "56f06885daf94b4fcc0a624b", +"snowflake": "56f06885daf94b4fcc0a624b", +"tsql": "56f06885daf94b4fcc0a624b" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "396b2ce32efb5365b393c230", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "396b2ce32efb5365b393c230", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "305aaf0b95cee629dd46380c", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "305aaf0b95cee629dd46380c", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "d1dc9a140dbe2eb1874c6ad7", +"None": "d1dc9a140dbe2eb1874c6ad7", +"bigquery": "d1dc9a140dbe2eb1874c6ad7", +"databricks": "d1dc9a140dbe2eb1874c6ad7", +"duckdb": "d1dc9a140dbe2eb1874c6ad7", +"generic": "d1dc9a140dbe2eb1874c6ad7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d1dc9a140dbe2eb1874c6ad7", +"postgres": "d1dc9a140dbe2eb1874c6ad7", +"postgresql": "d1dc9a140dbe2eb1874c6ad7", +"snowflake": "d1dc9a140dbe2eb1874c6ad7", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "4c78bdde0b2dfd77ab71c9e8", +"None": "4c78bdde0b2dfd77ab71c9e8", +"bigquery": "4c78bdde0b2dfd77ab71c9e8", +"databricks": "4c78bdde0b2dfd77ab71c9e8", +"duckdb": "4c78bdde0b2dfd77ab71c9e8", +"generic": "4c78bdde0b2dfd77ab71c9e8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4c78bdde0b2dfd77ab71c9e8", +"postgres": "4c78bdde0b2dfd77ab71c9e8", +"postgresql": "4c78bdde0b2dfd77ab71c9e8", +"snowflake": "4c78bdde0b2dfd77ab71c9e8", +"tsql": "4c78bdde0b2dfd77ab71c9e8" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "8a3f094f18fb4047f0febf59", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "8a3f094f18fb4047f0febf59", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "8a3f094f18fb4047f0febf59", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "8a3f094f18fb4047f0febf59", +"postgresql": "8a3f094f18fb4047f0febf59", +"snowflake": "850deea990b52b82b36a9848", +"tsql": "cc8534b198fe0c225bbffd8d" +} +], +[ +"type", +{ +"DuckDB": "f46cf4b4e131aa122b5b33d6", +"None": "f46cf4b4e131aa122b5b33d6", +"bigquery": "f46cf4b4e131aa122b5b33d6", +"databricks": "f46cf4b4e131aa122b5b33d6", +"duckdb": "f46cf4b4e131aa122b5b33d6", +"generic": "f46cf4b4e131aa122b5b33d6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f46cf4b4e131aa122b5b33d6", +"postgres": "f46cf4b4e131aa122b5b33d6", +"postgresql": "f46cf4b4e131aa122b5b33d6", +"snowflake": "f46cf4b4e131aa122b5b33d6", +"tsql": "f46cf4b4e131aa122b5b33d6" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "6eb49ceb88fcb2297831ca05", +"None": "6eb49ceb88fcb2297831ca05", +"bigquery": "6eb49ceb88fcb2297831ca05", +"databricks": "6eb49ceb88fcb2297831ca05", +"duckdb": "6eb49ceb88fcb2297831ca05", +"generic": "6eb49ceb88fcb2297831ca05", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6eb49ceb88fcb2297831ca05", +"postgres": "6eb49ceb88fcb2297831ca05", +"postgresql": "6eb49ceb88fcb2297831ca05", +"snowflake": "6eb49ceb88fcb2297831ca05", +"tsql": "6eb49ceb88fcb2297831ca05" +} +], +[ +"type", +{ +"DuckDB": "853b2c0ad6ea4565b1e301e8", +"None": "853b2c0ad6ea4565b1e301e8", +"bigquery": "853b2c0ad6ea4565b1e301e8", +"databricks": "853b2c0ad6ea4565b1e301e8", +"duckdb": "853b2c0ad6ea4565b1e301e8", +"generic": "853b2c0ad6ea4565b1e301e8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "853b2c0ad6ea4565b1e301e8", +"postgres": "853b2c0ad6ea4565b1e301e8", +"postgresql": "853b2c0ad6ea4565b1e301e8", +"snowflake": "853b2c0ad6ea4565b1e301e8", +"tsql": "853b2c0ad6ea4565b1e301e8" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "1558a2a87f510cca4baaad5b", +"None": "1558a2a87f510cca4baaad5b", +"bigquery": "1558a2a87f510cca4baaad5b", +"databricks": "1558a2a87f510cca4baaad5b", +"duckdb": "1558a2a87f510cca4baaad5b", +"generic": "1558a2a87f510cca4baaad5b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1558a2a87f510cca4baaad5b", +"postgres": "1558a2a87f510cca4baaad5b", +"postgresql": "1558a2a87f510cca4baaad5b", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "1558a2a87f510cca4baaad5b" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "d043825e95555167dc033b5f", +"None": "d043825e95555167dc033b5f", +"bigquery": "d043825e95555167dc033b5f", +"databricks": "d043825e95555167dc033b5f", +"duckdb": "d043825e95555167dc033b5f", +"generic": "d043825e95555167dc033b5f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d043825e95555167dc033b5f", +"postgres": "d043825e95555167dc033b5f", +"postgresql": "d043825e95555167dc033b5f", +"snowflake": "d043825e95555167dc033b5f", +"tsql": "d043825e95555167dc033b5f" +} +], +[ +"type", +{ +"DuckDB": "6a2162b5abe5a73734c47041", +"None": "6a2162b5abe5a73734c47041", +"bigquery": "266b57bc7351f5093413db07", +"databricks": "84e86bec7646b6ad19ceef59", +"duckdb": "6a2162b5abe5a73734c47041", +"generic": "6a2162b5abe5a73734c47041", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a2162b5abe5a73734c47041", +"postgres": "6a2162b5abe5a73734c47041", +"postgresql": "6a2162b5abe5a73734c47041", +"snowflake": "16d902e899040d0c0c9dcf58", +"tsql": "6a2162b5abe5a73734c47041" +} +], +[ +"type", +{ +"DuckDB": "56245289768b0fb155df9ffd", +"None": "56245289768b0fb155df9ffd", +"bigquery": "56245289768b0fb155df9ffd", +"databricks": "56245289768b0fb155df9ffd", +"duckdb": "56245289768b0fb155df9ffd", +"generic": "56245289768b0fb155df9ffd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "56245289768b0fb155df9ffd", +"postgres": "56245289768b0fb155df9ffd", +"postgresql": "56245289768b0fb155df9ffd", +"snowflake": "56245289768b0fb155df9ffd", +"tsql": "56245289768b0fb155df9ffd" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "362b5422259610d45387d0ab", +"None": "362b5422259610d45387d0ab", +"bigquery": "362b5422259610d45387d0ab", +"databricks": "362b5422259610d45387d0ab", +"duckdb": "362b5422259610d45387d0ab", +"generic": "362b5422259610d45387d0ab", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "362b5422259610d45387d0ab", +"postgres": "362b5422259610d45387d0ab", +"postgresql": "362b5422259610d45387d0ab", +"snowflake": "362b5422259610d45387d0ab", +"tsql": "362b5422259610d45387d0ab" +} +], +[ +"type", +{ +"DuckDB": "b2c9b6ebca6c097f3caa0aa8", +"None": "b2c9b6ebca6c097f3caa0aa8", +"bigquery": "b2c9b6ebca6c097f3caa0aa8", +"databricks": "b2c9b6ebca6c097f3caa0aa8", +"duckdb": "b2c9b6ebca6c097f3caa0aa8", +"generic": "b2c9b6ebca6c097f3caa0aa8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b2c9b6ebca6c097f3caa0aa8", +"postgres": "b2c9b6ebca6c097f3caa0aa8", +"postgresql": "b2c9b6ebca6c097f3caa0aa8", +"snowflake": "b2c9b6ebca6c097f3caa0aa8", +"tsql": "b2c9b6ebca6c097f3caa0aa8" +} +], +[ +"type", +{ +"DuckDB": "25f9faed41d5d23ca3fa8175", +"None": "25f9faed41d5d23ca3fa8175", +"bigquery": "122a82cc999a6e522d790b87", +"databricks": "b2c07944c578e7e9c9297bd8", +"duckdb": "25f9faed41d5d23ca3fa8175", +"generic": "25f9faed41d5d23ca3fa8175", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "25f9faed41d5d23ca3fa8175", +"postgres": "25f9faed41d5d23ca3fa8175", +"postgresql": "25f9faed41d5d23ca3fa8175", +"snowflake": "fc6c5aa7025d9a737c71c606", +"tsql": "4b6a4cbc7837ba2c70c2affb" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "78b2cf275c640af54feeca31", +"bigquery": "78b2cf275c640af54feeca31", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "78b2cf275c640af54feeca31", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "78b2cf275c640af54feeca31", +"postgresql": "78b2cf275c640af54feeca31", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "78b2cf275c640af54feeca31" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "f7fca17b74cc50ae3de8c826", +"bigquery": "f7fca17b74cc50ae3de8c826", +"databricks": "f7fca17b74cc50ae3de8c826", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "f7fca17b74cc50ae3de8c826", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f7fca17b74cc50ae3de8c826", +"postgres": "f7fca17b74cc50ae3de8c826", +"postgresql": "f7fca17b74cc50ae3de8c826", +"snowflake": "55155766823b6a3a5e882fc2", +"tsql": "f7fca17b74cc50ae3de8c826" +} +], +[ +"type", +{ +"DuckDB": "e7a76004da6461a939ce5fef", +"None": "e7a76004da6461a939ce5fef", +"bigquery": "e7a76004da6461a939ce5fef", +"databricks": "e7a76004da6461a939ce5fef", +"duckdb": "e7a76004da6461a939ce5fef", +"generic": "e7a76004da6461a939ce5fef", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e7a76004da6461a939ce5fef", +"postgres": "e7a76004da6461a939ce5fef", +"postgresql": "e7a76004da6461a939ce5fef", +"snowflake": "e7a76004da6461a939ce5fef", +"tsql": "e7a76004da6461a939ce5fef" +} +], +[ +"type", +{ +"DuckDB": "60a23b7dcae6c79f4de83068", +"None": "60a23b7dcae6c79f4de83068", +"bigquery": "60a23b7dcae6c79f4de83068", +"databricks": "60a23b7dcae6c79f4de83068", +"duckdb": "60a23b7dcae6c79f4de83068", +"generic": "60a23b7dcae6c79f4de83068", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60a23b7dcae6c79f4de83068", +"postgres": "60a23b7dcae6c79f4de83068", +"postgresql": "60a23b7dcae6c79f4de83068", +"snowflake": "60a23b7dcae6c79f4de83068", +"tsql": "60a23b7dcae6c79f4de83068" +} +], +[ +"type", +{ +"DuckDB": "a47af75c6582f749e1d2b2e7", +"None": "a47af75c6582f749e1d2b2e7", +"bigquery": "a47af75c6582f749e1d2b2e7", +"databricks": "a47af75c6582f749e1d2b2e7", +"duckdb": "a47af75c6582f749e1d2b2e7", +"generic": "a47af75c6582f749e1d2b2e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a47af75c6582f749e1d2b2e7", +"postgres": "a47af75c6582f749e1d2b2e7", +"postgresql": "a47af75c6582f749e1d2b2e7", +"snowflake": "a47af75c6582f749e1d2b2e7", +"tsql": "a47af75c6582f749e1d2b2e7" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "e1c74d96ac578f9323bf1bbd", +"None": "e1c74d96ac578f9323bf1bbd", +"bigquery": "e1c74d96ac578f9323bf1bbd", +"databricks": "e1c74d96ac578f9323bf1bbd", +"duckdb": "e1c74d96ac578f9323bf1bbd", +"generic": "e1c74d96ac578f9323bf1bbd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c74d96ac578f9323bf1bbd", +"postgres": "e1c74d96ac578f9323bf1bbd", +"postgresql": "e1c74d96ac578f9323bf1bbd", +"snowflake": "e1c74d96ac578f9323bf1bbd", +"tsql": "e1c74d96ac578f9323bf1bbd" +} +], +[ +"type", +{ +"DuckDB": "200c00626c910f6fd6177bb5", +"None": "200c00626c910f6fd6177bb5", +"bigquery": "200c00626c910f6fd6177bb5", +"databricks": "200c00626c910f6fd6177bb5", +"duckdb": "200c00626c910f6fd6177bb5", +"generic": "200c00626c910f6fd6177bb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "200c00626c910f6fd6177bb5", +"postgres": "200c00626c910f6fd6177bb5", +"postgresql": "200c00626c910f6fd6177bb5", +"snowflake": "200c00626c910f6fd6177bb5", +"tsql": "200c00626c910f6fd6177bb5" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "fe0bedb7dda0e9247a51fdee", +"None": "fe0bedb7dda0e9247a51fdee", +"bigquery": "fe0bedb7dda0e9247a51fdee", +"databricks": "fe0bedb7dda0e9247a51fdee", +"duckdb": "fe0bedb7dda0e9247a51fdee", +"generic": "fe0bedb7dda0e9247a51fdee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe0bedb7dda0e9247a51fdee", +"postgres": "fe0bedb7dda0e9247a51fdee", +"postgresql": "fe0bedb7dda0e9247a51fdee", +"snowflake": "fe0bedb7dda0e9247a51fdee", +"tsql": "fe0bedb7dda0e9247a51fdee" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "aed0419e7f2ce7c0a8892248", +"None": "aed0419e7f2ce7c0a8892248", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "aed0419e7f2ce7c0a8892248", +"generic": "aed0419e7f2ce7c0a8892248", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aed0419e7f2ce7c0a8892248", +"postgres": "19e127c23b4598727a2d8ceb", +"postgresql": "19e127c23b4598727a2d8ceb", +"snowflake": "aed0419e7f2ce7c0a8892248", +"tsql": "aed0419e7f2ce7c0a8892248" +} +], +[ +"type", +{ +"DuckDB": "55a086bc8305ad303017ae10", +"None": "55a086bc8305ad303017ae10", +"bigquery": "55a086bc8305ad303017ae10", +"databricks": "55a086bc8305ad303017ae10", +"duckdb": "55a086bc8305ad303017ae10", +"generic": "55a086bc8305ad303017ae10", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "55a086bc8305ad303017ae10", +"postgres": "55a086bc8305ad303017ae10", +"postgresql": "55a086bc8305ad303017ae10", +"snowflake": "55a086bc8305ad303017ae10", +"tsql": "55a086bc8305ad303017ae10" +} +], +[ +"type", +{ +"DuckDB": "f7598fbf399afabe1e01a358", +"None": "d08ef9483feed34fc0ee4319", +"bigquery": "d08ef9483feed34fc0ee4319", +"databricks": "d08ef9483feed34fc0ee4319", +"duckdb": "f7598fbf399afabe1e01a358", +"generic": "d08ef9483feed34fc0ee4319", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d08ef9483feed34fc0ee4319", +"postgres": "d08ef9483feed34fc0ee4319", +"postgresql": "d08ef9483feed34fc0ee4319", +"snowflake": "d08ef9483feed34fc0ee4319", +"tsql": "d08ef9483feed34fc0ee4319" +} +], +[ +"type", +{ +"DuckDB": "d3b6a6835220f9cedfa363c7", +"None": "d3b6a6835220f9cedfa363c7", +"bigquery": "d3b6a6835220f9cedfa363c7", +"databricks": "d3b6a6835220f9cedfa363c7", +"duckdb": "d3b6a6835220f9cedfa363c7", +"generic": "d3b6a6835220f9cedfa363c7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d3b6a6835220f9cedfa363c7", +"postgres": "d3b6a6835220f9cedfa363c7", +"postgresql": "d3b6a6835220f9cedfa363c7", +"snowflake": "d3b6a6835220f9cedfa363c7", +"tsql": "d3b6a6835220f9cedfa363c7" +} +], +[ +"type", +{ +"DuckDB": "3d23f811a03db519b598c815", +"None": "3d23f811a03db519b598c815", +"bigquery": "3d23f811a03db519b598c815", +"databricks": "3d23f811a03db519b598c815", +"duckdb": "3d23f811a03db519b598c815", +"generic": "3d23f811a03db519b598c815", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d23f811a03db519b598c815", +"postgres": "3d23f811a03db519b598c815", +"postgresql": "3d23f811a03db519b598c815", +"snowflake": "3d23f811a03db519b598c815", +"tsql": "3d23f811a03db519b598c815" +} +], +[ +"type", +{ +"DuckDB": "f816d7b3b97815f308346e20", +"None": "f816d7b3b97815f308346e20", +"bigquery": "f816d7b3b97815f308346e20", +"databricks": "f816d7b3b97815f308346e20", +"duckdb": "f816d7b3b97815f308346e20", +"generic": "f816d7b3b97815f308346e20", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f816d7b3b97815f308346e20", +"postgres": "f816d7b3b97815f308346e20", +"postgresql": "f816d7b3b97815f308346e20", +"snowflake": "f816d7b3b97815f308346e20", +"tsql": "f816d7b3b97815f308346e20" +} +], +[ +"type", +{ +"DuckDB": "aed0419e7f2ce7c0a8892248", +"None": "aed0419e7f2ce7c0a8892248", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "aed0419e7f2ce7c0a8892248", +"generic": "aed0419e7f2ce7c0a8892248", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aed0419e7f2ce7c0a8892248", +"postgres": "19e127c23b4598727a2d8ceb", +"postgresql": "19e127c23b4598727a2d8ceb", +"snowflake": "aed0419e7f2ce7c0a8892248", +"tsql": "aed0419e7f2ce7c0a8892248" +} +], +[ +"type", +{ +"DuckDB": "57922064a7dc546cbb50e42b", +"None": "57922064a7dc546cbb50e42b", +"bigquery": "57922064a7dc546cbb50e42b", +"databricks": "57922064a7dc546cbb50e42b", +"duckdb": "57922064a7dc546cbb50e42b", +"generic": "57922064a7dc546cbb50e42b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "57922064a7dc546cbb50e42b", +"postgres": "57922064a7dc546cbb50e42b", +"postgresql": "57922064a7dc546cbb50e42b", +"snowflake": "57922064a7dc546cbb50e42b", +"tsql": "57922064a7dc546cbb50e42b" +} +], +[ +"type", +{ +"DuckDB": "7b2fd741638cbd96bb38d7e0", +"None": "7b2fd741638cbd96bb38d7e0", +"bigquery": "7b2fd741638cbd96bb38d7e0", +"databricks": "4fb572024031cb7631777be1", +"duckdb": "7b2fd741638cbd96bb38d7e0", +"generic": "7b2fd741638cbd96bb38d7e0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7b2fd741638cbd96bb38d7e0", +"postgres": "7b2fd741638cbd96bb38d7e0", +"postgresql": "7b2fd741638cbd96bb38d7e0", +"snowflake": "7b2fd741638cbd96bb38d7e0", +"tsql": "7b2fd741638cbd96bb38d7e0" +} +], +[ +"type", +{ +"DuckDB": "a766fea06fc070b352976b0a", +"None": "a766fea06fc070b352976b0a", +"bigquery": "e4f0244beaa01667e80482b6", +"databricks": "e4f0244beaa01667e80482b6", +"duckdb": "a766fea06fc070b352976b0a", +"generic": "a766fea06fc070b352976b0a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e4f0244beaa01667e80482b6", +"postgres": "a766fea06fc070b352976b0a", +"postgresql": "a766fea06fc070b352976b0a", +"snowflake": "a766fea06fc070b352976b0a", +"tsql": "a766fea06fc070b352976b0a" +} +], +[ +"type", +{ +"DuckDB": "15b3387f6c4d73bcf113983a", +"None": "15b3387f6c4d73bcf113983a", +"bigquery": "15b3387f6c4d73bcf113983a", +"databricks": "15b3387f6c4d73bcf113983a", +"duckdb": "15b3387f6c4d73bcf113983a", +"generic": "15b3387f6c4d73bcf113983a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "15b3387f6c4d73bcf113983a", +"postgres": "15b3387f6c4d73bcf113983a", +"postgresql": "15b3387f6c4d73bcf113983a", +"snowflake": "15b3387f6c4d73bcf113983a", +"tsql": "15b3387f6c4d73bcf113983a" +} +], +[ +"type", +{ +"DuckDB": "d34f84c1b71b9c9c0c20abb0", +"None": "d34f84c1b71b9c9c0c20abb0", +"bigquery": "d34f84c1b71b9c9c0c20abb0", +"databricks": "c662d9470aa4255dfb493df1", +"duckdb": "d34f84c1b71b9c9c0c20abb0", +"generic": "d34f84c1b71b9c9c0c20abb0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c662d9470aa4255dfb493df1", +"postgres": "d34f84c1b71b9c9c0c20abb0", +"postgresql": "d34f84c1b71b9c9c0c20abb0", +"snowflake": "d34f84c1b71b9c9c0c20abb0", +"tsql": "d34f84c1b71b9c9c0c20abb0" +} +], +[ +"type", +{ +"DuckDB": "14cd6e78d75f61b2bd1bbd71", +"None": "14cd6e78d75f61b2bd1bbd71", +"bigquery": "14cd6e78d75f61b2bd1bbd71", +"databricks": "73504f31156e8e661c1fb26c", +"duckdb": "14cd6e78d75f61b2bd1bbd71", +"generic": "14cd6e78d75f61b2bd1bbd71", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73504f31156e8e661c1fb26c", +"postgres": "14cd6e78d75f61b2bd1bbd71", +"postgresql": "14cd6e78d75f61b2bd1bbd71", +"snowflake": "14cd6e78d75f61b2bd1bbd71", +"tsql": "14cd6e78d75f61b2bd1bbd71" +} +], +[ +"type", +{ +"DuckDB": "96e8f438bf42d79cd8b1fada", +"None": "96e8f438bf42d79cd8b1fada", +"bigquery": "96e8f438bf42d79cd8b1fada", +"databricks": "96e8f438bf42d79cd8b1fada", +"duckdb": "96e8f438bf42d79cd8b1fada", +"generic": "96e8f438bf42d79cd8b1fada", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "96e8f438bf42d79cd8b1fada", +"postgres": "96e8f438bf42d79cd8b1fada", +"postgresql": "96e8f438bf42d79cd8b1fada", +"snowflake": "96e8f438bf42d79cd8b1fada", +"tsql": "96e8f438bf42d79cd8b1fada" +} +], +[ +"type", +{ +"DuckDB": "260b29f255364b2044abe127", +"None": "260b29f255364b2044abe127", +"bigquery": "260b29f255364b2044abe127", +"databricks": "260b29f255364b2044abe127", +"duckdb": "260b29f255364b2044abe127", +"generic": "260b29f255364b2044abe127", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "260b29f255364b2044abe127", +"postgres": "260b29f255364b2044abe127", +"postgresql": "260b29f255364b2044abe127", +"snowflake": "260b29f255364b2044abe127", +"tsql": "260b29f255364b2044abe127" +} +], +[ +"type", +{ +"DuckDB": "e5199d22ebbd1383c1e821df", +"None": "e5199d22ebbd1383c1e821df", +"bigquery": "e5199d22ebbd1383c1e821df", +"databricks": "e5199d22ebbd1383c1e821df", +"duckdb": "e5199d22ebbd1383c1e821df", +"generic": "e5199d22ebbd1383c1e821df", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e5199d22ebbd1383c1e821df", +"postgres": "e5199d22ebbd1383c1e821df", +"postgresql": "e5199d22ebbd1383c1e821df", +"snowflake": "e5199d22ebbd1383c1e821df", +"tsql": "e5199d22ebbd1383c1e821df" +} +], +[ +"type", +{ +"DuckDB": "05ebf79d18b2ae4eaa41f450", +"None": "336fd1b7a1f7d8105c29280c", +"bigquery": "aa04c66b4fed5190c90308fd", +"databricks": "336fd1b7a1f7d8105c29280c", +"duckdb": "05ebf79d18b2ae4eaa41f450", +"generic": "336fd1b7a1f7d8105c29280c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "336fd1b7a1f7d8105c29280c", +"postgres": "336fd1b7a1f7d8105c29280c", +"postgresql": "336fd1b7a1f7d8105c29280c", +"snowflake": "336fd1b7a1f7d8105c29280c", +"tsql": "336fd1b7a1f7d8105c29280c" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "802f1c1fcb97ddd5679172d2", +"None": "802f1c1fcb97ddd5679172d2", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "802f1c1fcb97ddd5679172d2", +"duckdb": "802f1c1fcb97ddd5679172d2", +"generic": "802f1c1fcb97ddd5679172d2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "802f1c1fcb97ddd5679172d2", +"postgres": "802f1c1fcb97ddd5679172d2", +"postgresql": "802f1c1fcb97ddd5679172d2", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "08784560ad78e14a02cc066f" +} +], +[ +"type", +{ +"DuckDB": "d91c8f17eb347629690400c7", +"None": "d91c8f17eb347629690400c7", +"bigquery": "d91c8f17eb347629690400c7", +"databricks": "d91c8f17eb347629690400c7", +"duckdb": "d91c8f17eb347629690400c7", +"generic": "d91c8f17eb347629690400c7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d91c8f17eb347629690400c7", +"postgres": "d91c8f17eb347629690400c7", +"postgresql": "d91c8f17eb347629690400c7", +"snowflake": "d91c8f17eb347629690400c7", +"tsql": "d91c8f17eb347629690400c7" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "314ed506f24e92300a425780", +"None": "314ed506f24e92300a425780", +"bigquery": "314ed506f24e92300a425780", +"databricks": "314ed506f24e92300a425780", +"duckdb": "314ed506f24e92300a425780", +"generic": "314ed506f24e92300a425780", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "314ed506f24e92300a425780", +"postgres": "314ed506f24e92300a425780", +"postgresql": "314ed506f24e92300a425780", +"snowflake": "314ed506f24e92300a425780", +"tsql": "314ed506f24e92300a425780" +} +], +[ +"type", +{ +"DuckDB": "2873244018c69d6d42752bdc", +"None": "2873244018c69d6d42752bdc", +"bigquery": "2873244018c69d6d42752bdc", +"databricks": "2873244018c69d6d42752bdc", +"duckdb": "2873244018c69d6d42752bdc", +"generic": "2873244018c69d6d42752bdc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2873244018c69d6d42752bdc", +"postgres": "2873244018c69d6d42752bdc", +"postgresql": "2873244018c69d6d42752bdc", +"snowflake": "2873244018c69d6d42752bdc", +"tsql": "2873244018c69d6d42752bdc" +} +], +[ +"type", +{ +"DuckDB": "3d23f811a03db519b598c815", +"None": "3d23f811a03db519b598c815", +"bigquery": "3d23f811a03db519b598c815", +"databricks": "3d23f811a03db519b598c815", +"duckdb": "3d23f811a03db519b598c815", +"generic": "3d23f811a03db519b598c815", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d23f811a03db519b598c815", +"postgres": "3d23f811a03db519b598c815", +"postgresql": "3d23f811a03db519b598c815", +"snowflake": "3d23f811a03db519b598c815", +"tsql": "3d23f811a03db519b598c815" +} +], +[ +"type", +{ +"DuckDB": "087a9417cb511ff7bcfd6ccb", +"None": "383f8922f4b6f167d384037d", +"bigquery": "383f8922f4b6f167d384037d", +"databricks": "383f8922f4b6f167d384037d", +"duckdb": "087a9417cb511ff7bcfd6ccb", +"generic": "383f8922f4b6f167d384037d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "383f8922f4b6f167d384037d", +"postgres": "383f8922f4b6f167d384037d", +"postgresql": "383f8922f4b6f167d384037d", +"snowflake": "383f8922f4b6f167d384037d", +"tsql": "383f8922f4b6f167d384037d" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "b017fb3c48f125e51b6db123", +"None": "b017fb3c48f125e51b6db123", +"bigquery": "b017fb3c48f125e51b6db123", +"databricks": "b017fb3c48f125e51b6db123", +"duckdb": "b017fb3c48f125e51b6db123", +"generic": "b017fb3c48f125e51b6db123", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b017fb3c48f125e51b6db123", +"postgres": "b017fb3c48f125e51b6db123", +"postgresql": "b017fb3c48f125e51b6db123", +"snowflake": "b017fb3c48f125e51b6db123", +"tsql": "b017fb3c48f125e51b6db123" +} +], +[ +"type", +{ +"DuckDB": "a7120d1b1abcb844c87a968a", +"None": "a7120d1b1abcb844c87a968a", +"bigquery": "a7120d1b1abcb844c87a968a", +"databricks": "a7120d1b1abcb844c87a968a", +"duckdb": "a7120d1b1abcb844c87a968a", +"generic": "a7120d1b1abcb844c87a968a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a7120d1b1abcb844c87a968a", +"postgres": "a7120d1b1abcb844c87a968a", +"postgresql": "a7120d1b1abcb844c87a968a", +"snowflake": "a7120d1b1abcb844c87a968a", +"tsql": "a7120d1b1abcb844c87a968a" +} +], +[ +"type", +{ +"DuckDB": "fd31c8e468772027804119ec", +"None": "fd31c8e468772027804119ec", +"bigquery": "fd31c8e468772027804119ec", +"databricks": "fd31c8e468772027804119ec", +"duckdb": "fd31c8e468772027804119ec", +"generic": "fd31c8e468772027804119ec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fd31c8e468772027804119ec", +"postgres": "fd31c8e468772027804119ec", +"postgresql": "fd31c8e468772027804119ec", +"snowflake": "fd31c8e468772027804119ec", +"tsql": "fd31c8e468772027804119ec" +} +], +[ +"type", +{ +"DuckDB": "4169d9651c2a3667fc561f32", +"None": "4169d9651c2a3667fc561f32", +"bigquery": "88f7cced71c11944260bb54a", +"databricks": "4169d9651c2a3667fc561f32", +"duckdb": "4169d9651c2a3667fc561f32", +"generic": "4169d9651c2a3667fc561f32", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4169d9651c2a3667fc561f32", +"postgres": "4169d9651c2a3667fc561f32", +"postgresql": "4169d9651c2a3667fc561f32", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "4169d9651c2a3667fc561f32" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "eed0645c1e86fa1a6e35ccaa", +"None": "eed0645c1e86fa1a6e35ccaa", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "eed0645c1e86fa1a6e35ccaa", +"duckdb": "eed0645c1e86fa1a6e35ccaa", +"generic": "eed0645c1e86fa1a6e35ccaa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "eed0645c1e86fa1a6e35ccaa", +"postgres": "eed0645c1e86fa1a6e35ccaa", +"postgresql": "eed0645c1e86fa1a6e35ccaa", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "eed0645c1e86fa1a6e35ccaa" +} +], +[ +"type", +{ +"DuckDB": "3a7ffeb8f81df6ccd57f3689", +"None": "3a7ffeb8f81df6ccd57f3689", +"bigquery": "3a7ffeb8f81df6ccd57f3689", +"databricks": "3a7ffeb8f81df6ccd57f3689", +"duckdb": "3a7ffeb8f81df6ccd57f3689", +"generic": "3a7ffeb8f81df6ccd57f3689", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3a7ffeb8f81df6ccd57f3689", +"postgres": "3a7ffeb8f81df6ccd57f3689", +"postgresql": "3a7ffeb8f81df6ccd57f3689", +"snowflake": "3a7ffeb8f81df6ccd57f3689", +"tsql": "3a7ffeb8f81df6ccd57f3689" +} +], +[ +"type", +{ +"DuckDB": "e1c74d96ac578f9323bf1bbd", +"None": "e1c74d96ac578f9323bf1bbd", +"bigquery": "e1c74d96ac578f9323bf1bbd", +"databricks": "e1c74d96ac578f9323bf1bbd", +"duckdb": "e1c74d96ac578f9323bf1bbd", +"generic": "e1c74d96ac578f9323bf1bbd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c74d96ac578f9323bf1bbd", +"postgres": "e1c74d96ac578f9323bf1bbd", +"postgresql": "e1c74d96ac578f9323bf1bbd", +"snowflake": "e1c74d96ac578f9323bf1bbd", +"tsql": "e1c74d96ac578f9323bf1bbd" +} +], +[ +"type", +{ +"DuckDB": "e69f24e5693aeece1e1a03b8", +"None": "6dd6559eebacf5e375e1cce3", +"bigquery": "3e76afa61378ff263b617c7e", +"databricks": "e4317ab2bb471f29ee3c44be", +"duckdb": "e69f24e5693aeece1e1a03b8", +"generic": "6dd6559eebacf5e375e1cce3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6dd6559eebacf5e375e1cce3", +"postgres": "6dd6559eebacf5e375e1cce3", +"postgresql": "6dd6559eebacf5e375e1cce3", +"snowflake": "a4325ee9f4d61463eb81badb", +"tsql": "6f2f02617b722e48d3f365c2" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "26e762643db86e7646e57cba", +"None": "26e762643db86e7646e57cba", +"bigquery": "26e762643db86e7646e57cba", +"databricks": "26e762643db86e7646e57cba", +"duckdb": "26e762643db86e7646e57cba", +"generic": "26e762643db86e7646e57cba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "26e762643db86e7646e57cba", +"postgres": "26e762643db86e7646e57cba", +"postgresql": "26e762643db86e7646e57cba", +"snowflake": "26e762643db86e7646e57cba", +"tsql": "26e762643db86e7646e57cba" +} +], +[ +"type", +{ +"DuckDB": "078c0e4b25cdb89eb4ebd88f", +"None": "078c0e4b25cdb89eb4ebd88f", +"bigquery": "5226d7e2a62c7650a87d6e84", +"databricks": "078c0e4b25cdb89eb4ebd88f", +"duckdb": "078c0e4b25cdb89eb4ebd88f", +"generic": "078c0e4b25cdb89eb4ebd88f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "078c0e4b25cdb89eb4ebd88f", +"postgres": "078c0e4b25cdb89eb4ebd88f", +"postgresql": "078c0e4b25cdb89eb4ebd88f", +"snowflake": "6306b9e88f7b13dbb6a32c00", +"tsql": "078c0e4b25cdb89eb4ebd88f" +} +], +[ +"type", +{ +"DuckDB": "7cd31289a5b99b5a5e24492d", +"None": "7cd31289a5b99b5a5e24492d", +"bigquery": "7cd31289a5b99b5a5e24492d", +"databricks": "7cd31289a5b99b5a5e24492d", +"duckdb": "7cd31289a5b99b5a5e24492d", +"generic": "7cd31289a5b99b5a5e24492d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7cd31289a5b99b5a5e24492d", +"postgres": "7cd31289a5b99b5a5e24492d", +"postgresql": "7cd31289a5b99b5a5e24492d", +"snowflake": "7cd31289a5b99b5a5e24492d", +"tsql": "7cd31289a5b99b5a5e24492d" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "b7d6fc051a3c1ea6deb29ff4", +"postgresql": "b7d6fc051a3c1ea6deb29ff4", +"snowflake": "251aba3fef503ce64556c701", +"tsql": "e3bc1b8bad22d8104417b00f" +} +], +[ +"type", +{ +"DuckDB": "3eb44072dafa3bef75c97b6a", +"None": "3eb44072dafa3bef75c97b6a", +"bigquery": "3eb44072dafa3bef75c97b6a", +"databricks": "3eb44072dafa3bef75c97b6a", +"duckdb": "3eb44072dafa3bef75c97b6a", +"generic": "3eb44072dafa3bef75c97b6a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3eb44072dafa3bef75c97b6a", +"postgres": "3eb44072dafa3bef75c97b6a", +"postgresql": "3eb44072dafa3bef75c97b6a", +"snowflake": "3eb44072dafa3bef75c97b6a", +"tsql": "3eb44072dafa3bef75c97b6a" +} +], +[ +"type", +{ +"DuckDB": "596dafb6039bd1951f03c6a3", +"None": "596dafb6039bd1951f03c6a3", +"bigquery": "73f0a74496572926a274036e", +"databricks": "596dafb6039bd1951f03c6a3", +"duckdb": "596dafb6039bd1951f03c6a3", +"generic": "596dafb6039bd1951f03c6a3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "596dafb6039bd1951f03c6a3", +"postgres": "0d0c279c483ce8c07a9e7375", +"postgresql": "0d0c279c483ce8c07a9e7375", +"snowflake": "596dafb6039bd1951f03c6a3", +"tsql": "c100cdbd268164cf084295be" +} +], +[ +"type", +{ +"DuckDB": "b2c9b6ebca6c097f3caa0aa8", +"None": "b2c9b6ebca6c097f3caa0aa8", +"bigquery": "b2c9b6ebca6c097f3caa0aa8", +"databricks": "b2c9b6ebca6c097f3caa0aa8", +"duckdb": "b2c9b6ebca6c097f3caa0aa8", +"generic": "b2c9b6ebca6c097f3caa0aa8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b2c9b6ebca6c097f3caa0aa8", +"postgres": "b2c9b6ebca6c097f3caa0aa8", +"postgresql": "b2c9b6ebca6c097f3caa0aa8", +"snowflake": "b2c9b6ebca6c097f3caa0aa8", +"tsql": "b2c9b6ebca6c097f3caa0aa8" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "4d90ccac34ef123bb0b2a15c", +"None": "4d90ccac34ef123bb0b2a15c", +"bigquery": "4d90ccac34ef123bb0b2a15c", +"databricks": "4d90ccac34ef123bb0b2a15c", +"duckdb": "4d90ccac34ef123bb0b2a15c", +"generic": "4d90ccac34ef123bb0b2a15c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4d90ccac34ef123bb0b2a15c", +"postgres": "4d90ccac34ef123bb0b2a15c", +"postgresql": "4d90ccac34ef123bb0b2a15c", +"snowflake": "4d90ccac34ef123bb0b2a15c", +"tsql": "4d90ccac34ef123bb0b2a15c" +} +], +[ +"type", +{ +"DuckDB": "1ae302067c05dc9ae3a029f2", +"None": "1ae302067c05dc9ae3a029f2", +"bigquery": "1ae302067c05dc9ae3a029f2", +"databricks": "1ae302067c05dc9ae3a029f2", +"duckdb": "1ae302067c05dc9ae3a029f2", +"generic": "1ae302067c05dc9ae3a029f2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1ae302067c05dc9ae3a029f2", +"postgres": "1ae302067c05dc9ae3a029f2", +"postgresql": "1ae302067c05dc9ae3a029f2", +"snowflake": "1ae302067c05dc9ae3a029f2", +"tsql": "1ae302067c05dc9ae3a029f2" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "b1ec979036647e3f2686cece", +"None": "b1ec979036647e3f2686cece", +"bigquery": "b1ec979036647e3f2686cece", +"databricks": "b1ec979036647e3f2686cece", +"duckdb": "b1ec979036647e3f2686cece", +"generic": "b1ec979036647e3f2686cece", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b1ec979036647e3f2686cece", +"postgres": "b1ec979036647e3f2686cece", +"postgresql": "b1ec979036647e3f2686cece", +"snowflake": "b1ec979036647e3f2686cece", +"tsql": "b1ec979036647e3f2686cece" +} +], +[ +"type", +{ +"DuckDB": "962ea05bab624c1f8c259a65", +"None": "962ea05bab624c1f8c259a65", +"bigquery": "962ea05bab624c1f8c259a65", +"databricks": "962ea05bab624c1f8c259a65", +"duckdb": "962ea05bab624c1f8c259a65", +"generic": "962ea05bab624c1f8c259a65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "962ea05bab624c1f8c259a65", +"postgres": "962ea05bab624c1f8c259a65", +"postgresql": "962ea05bab624c1f8c259a65", +"snowflake": "962ea05bab624c1f8c259a65", +"tsql": "962ea05bab624c1f8c259a65" +} +], +[ +"type", +{ +"DuckDB": "f319941cc6ec088f46ecbadb", +"None": "f319941cc6ec088f46ecbadb", +"bigquery": "f319941cc6ec088f46ecbadb", +"databricks": "f319941cc6ec088f46ecbadb", +"duckdb": "f319941cc6ec088f46ecbadb", +"generic": "f319941cc6ec088f46ecbadb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f319941cc6ec088f46ecbadb", +"postgres": "f319941cc6ec088f46ecbadb", +"postgresql": "f319941cc6ec088f46ecbadb", +"snowflake": "f319941cc6ec088f46ecbadb", +"tsql": "f319941cc6ec088f46ecbadb" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "717cf79ef777823eb3576deb", +"None": "717cf79ef777823eb3576deb", +"bigquery": "717cf79ef777823eb3576deb", +"databricks": "717cf79ef777823eb3576deb", +"duckdb": "717cf79ef777823eb3576deb", +"generic": "717cf79ef777823eb3576deb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "717cf79ef777823eb3576deb", +"postgres": "717cf79ef777823eb3576deb", +"postgresql": "717cf79ef777823eb3576deb", +"snowflake": "717cf79ef777823eb3576deb", +"tsql": "717cf79ef777823eb3576deb" +} +], +[ +"type", +{ +"DuckDB": "8362a1ff79ce7c9350d11210", +"None": "8362a1ff79ce7c9350d11210", +"bigquery": "8362a1ff79ce7c9350d11210", +"databricks": "8362a1ff79ce7c9350d11210", +"duckdb": "8362a1ff79ce7c9350d11210", +"generic": "8362a1ff79ce7c9350d11210", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8362a1ff79ce7c9350d11210", +"postgres": "8362a1ff79ce7c9350d11210", +"postgresql": "8362a1ff79ce7c9350d11210", +"snowflake": "8362a1ff79ce7c9350d11210", +"tsql": "8362a1ff79ce7c9350d11210" +} +], +[ +"type", +{ +"DuckDB": "688179272da645629077e980", +"None": "688179272da645629077e980", +"bigquery": "688179272da645629077e980", +"databricks": "688179272da645629077e980", +"duckdb": "688179272da645629077e980", +"generic": "688179272da645629077e980", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "688179272da645629077e980", +"postgres": "688179272da645629077e980", +"postgresql": "688179272da645629077e980", +"snowflake": "b210f8eb26c37b422f786c6b", +"tsql": "688179272da645629077e980" +} +], +[ +"type", +{ +"DuckDB": "2e5b1ffe62d65d4d8c389ecd", +"None": "2e5b1ffe62d65d4d8c389ecd", +"bigquery": "2e5b1ffe62d65d4d8c389ecd", +"databricks": "2e5b1ffe62d65d4d8c389ecd", +"duckdb": "2e5b1ffe62d65d4d8c389ecd", +"generic": "2e5b1ffe62d65d4d8c389ecd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2e5b1ffe62d65d4d8c389ecd", +"postgres": "2e5b1ffe62d65d4d8c389ecd", +"postgresql": "2e5b1ffe62d65d4d8c389ecd", +"snowflake": "2e5b1ffe62d65d4d8c389ecd", +"tsql": "2e5b1ffe62d65d4d8c389ecd" +} +], +[ +"type", +{ +"DuckDB": "e6badc927d1adf4563b4f3a5", +"None": "e6badc927d1adf4563b4f3a5", +"bigquery": "e6badc927d1adf4563b4f3a5", +"databricks": "e6badc927d1adf4563b4f3a5", +"duckdb": "e6badc927d1adf4563b4f3a5", +"generic": "e6badc927d1adf4563b4f3a5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e6badc927d1adf4563b4f3a5", +"postgres": "e6badc927d1adf4563b4f3a5", +"postgresql": "e6badc927d1adf4563b4f3a5", +"snowflake": "e6badc927d1adf4563b4f3a5", +"tsql": "e6badc927d1adf4563b4f3a5" +} +], +[ +"type", +{ +"DuckDB": "9070740b5c7f7fb5d091ad05", +"None": "9070740b5c7f7fb5d091ad05", +"bigquery": "9070740b5c7f7fb5d091ad05", +"databricks": "9070740b5c7f7fb5d091ad05", +"duckdb": "9070740b5c7f7fb5d091ad05", +"generic": "9070740b5c7f7fb5d091ad05", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9070740b5c7f7fb5d091ad05", +"postgres": "9070740b5c7f7fb5d091ad05", +"postgresql": "9070740b5c7f7fb5d091ad05", +"snowflake": "9070740b5c7f7fb5d091ad05", +"tsql": "9070740b5c7f7fb5d091ad05" +} +], +[ +"type", +{ +"DuckDB": "607f7c82658f39cce394b124", +"None": "607f7c82658f39cce394b124", +"bigquery": "607f7c82658f39cce394b124", +"databricks": "085a20ac29ebf425cac9e1d7", +"duckdb": "607f7c82658f39cce394b124", +"generic": "607f7c82658f39cce394b124", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "085a20ac29ebf425cac9e1d7", +"postgres": "607f7c82658f39cce394b124", +"postgresql": "607f7c82658f39cce394b124", +"snowflake": "607f7c82658f39cce394b124", +"tsql": "607f7c82658f39cce394b124" +} +], +[ +"type", +{ +"DuckDB": "b3d92948d21d50dcbeb5f80b", +"None": "b3d92948d21d50dcbeb5f80b", +"bigquery": "b3d92948d21d50dcbeb5f80b", +"databricks": "b3d92948d21d50dcbeb5f80b", +"duckdb": "b3d92948d21d50dcbeb5f80b", +"generic": "b3d92948d21d50dcbeb5f80b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b3d92948d21d50dcbeb5f80b", +"postgres": "b3d92948d21d50dcbeb5f80b", +"postgresql": "b3d92948d21d50dcbeb5f80b", +"snowflake": "b3d92948d21d50dcbeb5f80b", +"tsql": "b3d92948d21d50dcbeb5f80b" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "f4c43a87a78970bca1f88b09", +"None": "f4c43a87a78970bca1f88b09", +"bigquery": "f4c43a87a78970bca1f88b09", +"databricks": "f4c43a87a78970bca1f88b09", +"duckdb": "f4c43a87a78970bca1f88b09", +"generic": "f4c43a87a78970bca1f88b09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f4c43a87a78970bca1f88b09", +"postgres": "f4c43a87a78970bca1f88b09", +"postgresql": "f4c43a87a78970bca1f88b09", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f4c43a87a78970bca1f88b09" +} +], +[ +"type", +{ +"DuckDB": "2279ed9a279f48d8451671cf", +"None": "2279ed9a279f48d8451671cf", +"bigquery": "2279ed9a279f48d8451671cf", +"databricks": "2279ed9a279f48d8451671cf", +"duckdb": "2279ed9a279f48d8451671cf", +"generic": "2279ed9a279f48d8451671cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2279ed9a279f48d8451671cf", +"postgres": "2279ed9a279f48d8451671cf", +"postgresql": "2279ed9a279f48d8451671cf", +"snowflake": "2279ed9a279f48d8451671cf", +"tsql": "2279ed9a279f48d8451671cf" +} +], +[ +"type", +{ +"DuckDB": "881106a4a0d2de0ff7c56373", +"None": "881106a4a0d2de0ff7c56373", +"bigquery": "881106a4a0d2de0ff7c56373", +"databricks": "881106a4a0d2de0ff7c56373", +"duckdb": "881106a4a0d2de0ff7c56373", +"generic": "881106a4a0d2de0ff7c56373", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "881106a4a0d2de0ff7c56373", +"postgres": "881106a4a0d2de0ff7c56373", +"postgresql": "881106a4a0d2de0ff7c56373", +"snowflake": "881106a4a0d2de0ff7c56373", +"tsql": "881106a4a0d2de0ff7c56373" +} +], +[ +"type", +{ +"DuckDB": "f46cf4b4e131aa122b5b33d6", +"None": "f46cf4b4e131aa122b5b33d6", +"bigquery": "f46cf4b4e131aa122b5b33d6", +"databricks": "f46cf4b4e131aa122b5b33d6", +"duckdb": "f46cf4b4e131aa122b5b33d6", +"generic": "f46cf4b4e131aa122b5b33d6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f46cf4b4e131aa122b5b33d6", +"postgres": "f46cf4b4e131aa122b5b33d6", +"postgresql": "f46cf4b4e131aa122b5b33d6", +"snowflake": "f46cf4b4e131aa122b5b33d6", +"tsql": "f46cf4b4e131aa122b5b33d6" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "0475b57e9e9da81e87fa09ba", +"None": "0475b57e9e9da81e87fa09ba", +"bigquery": "0475b57e9e9da81e87fa09ba", +"databricks": "0475b57e9e9da81e87fa09ba", +"duckdb": "0475b57e9e9da81e87fa09ba", +"generic": "0475b57e9e9da81e87fa09ba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0475b57e9e9da81e87fa09ba", +"postgres": "0475b57e9e9da81e87fa09ba", +"postgresql": "0475b57e9e9da81e87fa09ba", +"snowflake": "0475b57e9e9da81e87fa09ba", +"tsql": "0475b57e9e9da81e87fa09ba" +} +], +[ +"type", +{ +"DuckDB": "43e3e82e80c9c1580eb61a1d", +"None": "43e3e82e80c9c1580eb61a1d", +"bigquery": "43e3e82e80c9c1580eb61a1d", +"databricks": "43e3e82e80c9c1580eb61a1d", +"duckdb": "43e3e82e80c9c1580eb61a1d", +"generic": "43e3e82e80c9c1580eb61a1d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "43e3e82e80c9c1580eb61a1d", +"postgres": "43e3e82e80c9c1580eb61a1d", +"postgresql": "43e3e82e80c9c1580eb61a1d", +"snowflake": "43e3e82e80c9c1580eb61a1d", +"tsql": "43e3e82e80c9c1580eb61a1d" +} +], +[ +"type", +{ +"DuckDB": "51b0e597909858f7f7aa4571", +"None": "51b0e597909858f7f7aa4571", +"bigquery": "109f87d65cfb16897db6a7a6", +"databricks": "846f4ee64c34aab0cdabb9a0", +"duckdb": "51b0e597909858f7f7aa4571", +"generic": "51b0e597909858f7f7aa4571", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "51b0e597909858f7f7aa4571", +"postgres": "51b0e597909858f7f7aa4571", +"postgresql": "51b0e597909858f7f7aa4571", +"snowflake": "4d94d72d93a870dcc1521bee", +"tsql": "51b0e597909858f7f7aa4571" +} +], +[ +"type", +{ +"DuckDB": "962ea05bab624c1f8c259a65", +"None": "962ea05bab624c1f8c259a65", +"bigquery": "962ea05bab624c1f8c259a65", +"databricks": "962ea05bab624c1f8c259a65", +"duckdb": "962ea05bab624c1f8c259a65", +"generic": "962ea05bab624c1f8c259a65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "962ea05bab624c1f8c259a65", +"postgres": "962ea05bab624c1f8c259a65", +"postgresql": "962ea05bab624c1f8c259a65", +"snowflake": "962ea05bab624c1f8c259a65", +"tsql": "962ea05bab624c1f8c259a65" +} +], +[ +"type", +{ +"DuckDB": "1275b5b0c52f69cf592c004b", +"None": "1275b5b0c52f69cf592c004b", +"bigquery": "1275b5b0c52f69cf592c004b", +"databricks": "1275b5b0c52f69cf592c004b", +"duckdb": "1275b5b0c52f69cf592c004b", +"generic": "1275b5b0c52f69cf592c004b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1275b5b0c52f69cf592c004b", +"postgres": "1275b5b0c52f69cf592c004b", +"postgresql": "1275b5b0c52f69cf592c004b", +"snowflake": "1275b5b0c52f69cf592c004b", +"tsql": "1275b5b0c52f69cf592c004b" +} +], +[ +"type", +{ +"DuckDB": "a01889d0707cf6b364efb5f9", +"None": "94834ae92073b4606184619b", +"bigquery": "11d9e4471b50590a198e94a1", +"databricks": "11d9e4471b50590a198e94a1", +"duckdb": "a01889d0707cf6b364efb5f9", +"generic": "94834ae92073b4606184619b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "11d9e4471b50590a198e94a1", +"postgres": "a01889d0707cf6b364efb5f9", +"postgresql": "a01889d0707cf6b364efb5f9", +"snowflake": "5bc09c1a33ccb4bd2e4075e5", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "c43574432afc90bcba6944b2", +"None": "bf431bf304d57dd7b16797df", +"bigquery": "bf431bf304d57dd7b16797df", +"databricks": "bf431bf304d57dd7b16797df", +"duckdb": "c43574432afc90bcba6944b2", +"generic": "bf431bf304d57dd7b16797df", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "b462303a5e5a209df5a75e4b", +"postgresql": "b462303a5e5a209df5a75e4b", +"snowflake": "26094c8570001998e247761e", +"tsql": "ba3896413716dbcd432e62dd" +} +], +[ +"type", +{ +"DuckDB": "66f7ca02161349d637fa7d4e", +"None": "66f7ca02161349d637fa7d4e", +"bigquery": "d90a78129f7050a540984f6f", +"databricks": "d90a78129f7050a540984f6f", +"duckdb": "66f7ca02161349d637fa7d4e", +"generic": "66f7ca02161349d637fa7d4e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d90a78129f7050a540984f6f", +"postgres": "66f7ca02161349d637fa7d4e", +"postgresql": "66f7ca02161349d637fa7d4e", +"snowflake": "66f7ca02161349d637fa7d4e", +"tsql": "66f7ca02161349d637fa7d4e" +} +], +[ +"type", +{ +"DuckDB": "83fdbd1f0a6ee4e72faa4d94", +"None": "83fdbd1f0a6ee4e72faa4d94", +"bigquery": "83fdbd1f0a6ee4e72faa4d94", +"databricks": "83fdbd1f0a6ee4e72faa4d94", +"duckdb": "83fdbd1f0a6ee4e72faa4d94", +"generic": "83fdbd1f0a6ee4e72faa4d94", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "83fdbd1f0a6ee4e72faa4d94", +"postgres": "83fdbd1f0a6ee4e72faa4d94", +"postgresql": "83fdbd1f0a6ee4e72faa4d94", +"snowflake": "83fdbd1f0a6ee4e72faa4d94", +"tsql": "83fdbd1f0a6ee4e72faa4d94" +} +], +[ +"type", +{ +"DuckDB": "e2879cf4770e7f48d2dd704a", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "e2879cf4770e7f48d2dd704a", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "7c8df74df226e73b459bfdba", +"None": "7c8df74df226e73b459bfdba", +"bigquery": "7c8df74df226e73b459bfdba", +"databricks": "7c8df74df226e73b459bfdba", +"duckdb": "7c8df74df226e73b459bfdba", +"generic": "7c8df74df226e73b459bfdba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7c8df74df226e73b459bfdba", +"postgres": "7c8df74df226e73b459bfdba", +"postgresql": "7c8df74df226e73b459bfdba", +"snowflake": "7c8df74df226e73b459bfdba", +"tsql": "7c8df74df226e73b459bfdba" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "267fe8533e5d50f2cd3eb176", +"None": "8e0e269b33b63f52e24399e2", +"bigquery": "6a9a0937e74ae8ee83e705e4", +"databricks": "6a9a0937e74ae8ee83e705e4", +"duckdb": "267fe8533e5d50f2cd3eb176", +"generic": "8e0e269b33b63f52e24399e2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "43767924372e86845ec63bb6", +"postgresql": "43767924372e86845ec63bb6", +"snowflake": "5a7da03d64d9bf65f9984782", +"tsql": "69ccced5b5ccc61253f2656b" +} +], +[ +"type", +{ +"DuckDB": "4920de3b1203a0d66fc1b3cd", +"None": "4920de3b1203a0d66fc1b3cd", +"bigquery": "4920de3b1203a0d66fc1b3cd", +"databricks": "4920de3b1203a0d66fc1b3cd", +"duckdb": "4920de3b1203a0d66fc1b3cd", +"generic": "4920de3b1203a0d66fc1b3cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4920de3b1203a0d66fc1b3cd", +"postgres": "4920de3b1203a0d66fc1b3cd", +"postgresql": "4920de3b1203a0d66fc1b3cd", +"snowflake": "4920de3b1203a0d66fc1b3cd", +"tsql": "4920de3b1203a0d66fc1b3cd" +} +], +[ +"type", +{ +"DuckDB": "7914189ff18b0e70ddf7d042", +"None": "7914189ff18b0e70ddf7d042", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "7914189ff18b0e70ddf7d042", +"duckdb": "7914189ff18b0e70ddf7d042", +"generic": "7914189ff18b0e70ddf7d042", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7914189ff18b0e70ddf7d042", +"postgres": "7914189ff18b0e70ddf7d042", +"postgresql": "7914189ff18b0e70ddf7d042", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "7914189ff18b0e70ddf7d042" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "4d0f0db1a550ed1e0da1a043", +"None": "4d0f0db1a550ed1e0da1a043", +"bigquery": "4d0f0db1a550ed1e0da1a043", +"databricks": "4d0f0db1a550ed1e0da1a043", +"duckdb": "4d0f0db1a550ed1e0da1a043", +"generic": "4d0f0db1a550ed1e0da1a043", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4d0f0db1a550ed1e0da1a043", +"postgres": "4d0f0db1a550ed1e0da1a043", +"postgresql": "4d0f0db1a550ed1e0da1a043", +"snowflake": "4d0f0db1a550ed1e0da1a043", +"tsql": "4d0f0db1a550ed1e0da1a043" +} +], +[ +"type", +{ +"DuckDB": "a9d9e03f8e981cec9dca7694", +"None": "a9d9e03f8e981cec9dca7694", +"bigquery": "a9d9e03f8e981cec9dca7694", +"databricks": "a9d9e03f8e981cec9dca7694", +"duckdb": "a9d9e03f8e981cec9dca7694", +"generic": "a9d9e03f8e981cec9dca7694", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a9d9e03f8e981cec9dca7694", +"postgres": "a9d9e03f8e981cec9dca7694", +"postgresql": "a9d9e03f8e981cec9dca7694", +"snowflake": "a9d9e03f8e981cec9dca7694", +"tsql": "a9d9e03f8e981cec9dca7694" +} +], +[ +"type", +{ +"DuckDB": "95961ec6c293c3d6b6bf65ec", +"None": "95961ec6c293c3d6b6bf65ec", +"bigquery": "95961ec6c293c3d6b6bf65ec", +"databricks": "95961ec6c293c3d6b6bf65ec", +"duckdb": "95961ec6c293c3d6b6bf65ec", +"generic": "95961ec6c293c3d6b6bf65ec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "95961ec6c293c3d6b6bf65ec", +"postgres": "95961ec6c293c3d6b6bf65ec", +"postgresql": "95961ec6c293c3d6b6bf65ec", +"snowflake": "95961ec6c293c3d6b6bf65ec", +"tsql": "95961ec6c293c3d6b6bf65ec" +} +], +[ +"type", +{ +"DuckDB": "2c5e4442c23cf9e2b37caa7e", +"None": "2c5e4442c23cf9e2b37caa7e", +"bigquery": "2c5e4442c23cf9e2b37caa7e", +"databricks": "2c5e4442c23cf9e2b37caa7e", +"duckdb": "2c5e4442c23cf9e2b37caa7e", +"generic": "2c5e4442c23cf9e2b37caa7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c5e4442c23cf9e2b37caa7e", +"postgres": "2c5e4442c23cf9e2b37caa7e", +"postgresql": "2c5e4442c23cf9e2b37caa7e", +"snowflake": "2c5e4442c23cf9e2b37caa7e", +"tsql": "2c5e4442c23cf9e2b37caa7e" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "8702414b6372d27d90111c0d", +"None": "a7fcdf83ba95c5481cac4059", +"bigquery": "1ebb508c9c012fda785eac26", +"databricks": "1ebb508c9c012fda785eac26", +"duckdb": "8702414b6372d27d90111c0d", +"generic": "a7fcdf83ba95c5481cac4059", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1ebb508c9c012fda785eac26", +"postgres": "8702414b6372d27d90111c0d", +"postgresql": "8702414b6372d27d90111c0d", +"snowflake": "521d89d8efac986351afc368", +"tsql": "1ebb508c9c012fda785eac26" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "72bd5e38b32893397eb51579", +"None": "730d7cf7510ba0934828d971", +"bigquery": "730d7cf7510ba0934828d971", +"databricks": "730d7cf7510ba0934828d971", +"duckdb": "72bd5e38b32893397eb51579", +"generic": "730d7cf7510ba0934828d971", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1bdf5908ca249b2331a0427c", +"postgres": "1bdf5908ca249b2331a0427c", +"postgresql": "1bdf5908ca249b2331a0427c", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "1bdf5908ca249b2331a0427c" +} +], +[ +"type", +{ +"DuckDB": "dd061dac05d8a112fce62140", +"None": "dd061dac05d8a112fce62140", +"bigquery": "dd061dac05d8a112fce62140", +"databricks": "dd061dac05d8a112fce62140", +"duckdb": "dd061dac05d8a112fce62140", +"generic": "dd061dac05d8a112fce62140", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dd061dac05d8a112fce62140", +"postgres": "dd061dac05d8a112fce62140", +"postgresql": "dd061dac05d8a112fce62140", +"snowflake": "dd061dac05d8a112fce62140", +"tsql": "dd061dac05d8a112fce62140" +} +], +[ +"type", +{ +"DuckDB": "d7b4d15e2e3140069c23e6d4", +"None": "d7b4d15e2e3140069c23e6d4", +"bigquery": "ba82290eb0d38f9638b8b0dc", +"databricks": "9ce61cdfd35b2a8b31faa983", +"duckdb": "d7b4d15e2e3140069c23e6d4", +"generic": "d7b4d15e2e3140069c23e6d4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d7b4d15e2e3140069c23e6d4", +"postgres": "d7b4d15e2e3140069c23e6d4", +"postgresql": "d7b4d15e2e3140069c23e6d4", +"snowflake": "906644f12c56f91f8767953d", +"tsql": "d7b4d15e2e3140069c23e6d4" +} +], +[ +"type", +{ +"DuckDB": "cd932c5de0731b5ff7bddd24", +"None": "cd932c5de0731b5ff7bddd24", +"bigquery": "cd932c5de0731b5ff7bddd24", +"databricks": "cd932c5de0731b5ff7bddd24", +"duckdb": "cd932c5de0731b5ff7bddd24", +"generic": "cd932c5de0731b5ff7bddd24", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cd932c5de0731b5ff7bddd24", +"postgres": "cd932c5de0731b5ff7bddd24", +"postgresql": "cd932c5de0731b5ff7bddd24", +"snowflake": "cd932c5de0731b5ff7bddd24", +"tsql": "cd932c5de0731b5ff7bddd24" +} +], +[ +"type", +{ +"DuckDB": "104d3e046c47032c37f947b8", +"None": "104d3e046c47032c37f947b8", +"bigquery": "104d3e046c47032c37f947b8", +"databricks": "104d3e046c47032c37f947b8", +"duckdb": "104d3e046c47032c37f947b8", +"generic": "104d3e046c47032c37f947b8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "104d3e046c47032c37f947b8", +"postgres": "104d3e046c47032c37f947b8", +"postgresql": "104d3e046c47032c37f947b8", +"snowflake": "104d3e046c47032c37f947b8", +"tsql": "104d3e046c47032c37f947b8" +} +], +[ +"type", +{ +"DuckDB": "f179445103bc721a2dc04002", +"None": "f179445103bc721a2dc04002", +"bigquery": "f179445103bc721a2dc04002", +"databricks": "f179445103bc721a2dc04002", +"duckdb": "f179445103bc721a2dc04002", +"generic": "f179445103bc721a2dc04002", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f179445103bc721a2dc04002", +"postgres": "f179445103bc721a2dc04002", +"postgresql": "f179445103bc721a2dc04002", +"snowflake": "f179445103bc721a2dc04002", +"tsql": "f179445103bc721a2dc04002" +} +], +[ +"type", +{ +"DuckDB": "7a924f1a13854aa3260f3f57", +"None": "7a924f1a13854aa3260f3f57", +"bigquery": "7a924f1a13854aa3260f3f57", +"databricks": "7a924f1a13854aa3260f3f57", +"duckdb": "7a924f1a13854aa3260f3f57", +"generic": "7a924f1a13854aa3260f3f57", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7a924f1a13854aa3260f3f57", +"postgres": "7a924f1a13854aa3260f3f57", +"postgresql": "7a924f1a13854aa3260f3f57", +"snowflake": "7a924f1a13854aa3260f3f57", +"tsql": "7a924f1a13854aa3260f3f57" +} +], +[ +"type", +{ +"DuckDB": "3d4143f0295abc5ed5a3d8cc", +"None": "3d4143f0295abc5ed5a3d8cc", +"bigquery": "3d4143f0295abc5ed5a3d8cc", +"databricks": "3d4143f0295abc5ed5a3d8cc", +"duckdb": "3d4143f0295abc5ed5a3d8cc", +"generic": "3d4143f0295abc5ed5a3d8cc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d4143f0295abc5ed5a3d8cc", +"postgres": "3d4143f0295abc5ed5a3d8cc", +"postgresql": "3d4143f0295abc5ed5a3d8cc", +"snowflake": "3d4143f0295abc5ed5a3d8cc", +"tsql": "3d4143f0295abc5ed5a3d8cc" +} +], +[ +"type", +{ +"DuckDB": "3f71ab5f3c1c040da96f2cec", +"None": "3f71ab5f3c1c040da96f2cec", +"bigquery": "3f71ab5f3c1c040da96f2cec", +"databricks": "3f71ab5f3c1c040da96f2cec", +"duckdb": "3f71ab5f3c1c040da96f2cec", +"generic": "3f71ab5f3c1c040da96f2cec", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3f71ab5f3c1c040da96f2cec", +"postgres": "3f71ab5f3c1c040da96f2cec", +"postgresql": "3f71ab5f3c1c040da96f2cec", +"snowflake": "3f71ab5f3c1c040da96f2cec", +"tsql": "3f71ab5f3c1c040da96f2cec" +} +], +[ +"type", +{ +"DuckDB": "d4a375fcf7c3190951992469", +"None": "d4a375fcf7c3190951992469", +"bigquery": "5226d7e2a62c7650a87d6e84", +"databricks": "d4a375fcf7c3190951992469", +"duckdb": "d4a375fcf7c3190951992469", +"generic": "d4a375fcf7c3190951992469", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d4a375fcf7c3190951992469", +"postgres": "d4a375fcf7c3190951992469", +"postgresql": "d4a375fcf7c3190951992469", +"snowflake": "6306b9e88f7b13dbb6a32c00", +"tsql": "d4a375fcf7c3190951992469" +} +], +[ +"type", +{ +"DuckDB": "78709b3317abd96cc20340f7", +"None": "78709b3317abd96cc20340f7", +"bigquery": "78709b3317abd96cc20340f7", +"databricks": "78709b3317abd96cc20340f7", +"duckdb": "78709b3317abd96cc20340f7", +"generic": "78709b3317abd96cc20340f7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "78709b3317abd96cc20340f7", +"postgres": "78709b3317abd96cc20340f7", +"postgresql": "78709b3317abd96cc20340f7", +"snowflake": "78709b3317abd96cc20340f7", +"tsql": "78709b3317abd96cc20340f7" +} +], +[ +"type", +{ +"DuckDB": "652ff8b21f638eac8fdc46c5", +"None": "652ff8b21f638eac8fdc46c5", +"bigquery": "316ab5126bbd68359eedc4d9", +"databricks": "652ff8b21f638eac8fdc46c5", +"duckdb": "652ff8b21f638eac8fdc46c5", +"generic": "652ff8b21f638eac8fdc46c5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "652ff8b21f638eac8fdc46c5", +"postgres": "652ff8b21f638eac8fdc46c5", +"postgresql": "652ff8b21f638eac8fdc46c5", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "652ff8b21f638eac8fdc46c5" +} +], +[ +"type", +{ +"DuckDB": "293fdb322059805900912bee", +"None": "293fdb322059805900912bee", +"bigquery": "293fdb322059805900912bee", +"databricks": "293fdb322059805900912bee", +"duckdb": "293fdb322059805900912bee", +"generic": "293fdb322059805900912bee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "293fdb322059805900912bee", +"postgres": "293fdb322059805900912bee", +"postgresql": "293fdb322059805900912bee", +"snowflake": "293fdb322059805900912bee", +"tsql": "293fdb322059805900912bee" +} +], +[ +"type", +{ +"DuckDB": "3d23f811a03db519b598c815", +"None": "3d23f811a03db519b598c815", +"bigquery": "3d23f811a03db519b598c815", +"databricks": "3d23f811a03db519b598c815", +"duckdb": "3d23f811a03db519b598c815", +"generic": "3d23f811a03db519b598c815", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3d23f811a03db519b598c815", +"postgres": "3d23f811a03db519b598c815", +"postgresql": "3d23f811a03db519b598c815", +"snowflake": "3d23f811a03db519b598c815", +"tsql": "3d23f811a03db519b598c815" +} +], +[ +"type", +{ +"DuckDB": "9cf8454127727005f919e6fc", +"None": "9a5c2bd43f94970e2d4d2210", +"bigquery": "2cf01a5fea56869633f3f547", +"databricks": "1231fdcee1164056bb998fd8", +"duckdb": "9cf8454127727005f919e6fc", +"generic": "9a5c2bd43f94970e2d4d2210", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "84e2dbd148c2ae9480447652", +"postgres": "eaa1040a087ac34088fbabf6", +"postgresql": "eaa1040a087ac34088fbabf6", +"snowflake": "143732e0372eaf73cb82cfe7", +"tsql": "bb985bf3c283d9246c714eec" +} +], +[ +"type", +{ +"DuckDB": "2166772bfafca596e1de9d10", +"None": "2166772bfafca596e1de9d10", +"bigquery": "2166772bfafca596e1de9d10", +"databricks": "2166772bfafca596e1de9d10", +"duckdb": "2166772bfafca596e1de9d10", +"generic": "2166772bfafca596e1de9d10", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2166772bfafca596e1de9d10", +"postgres": "2166772bfafca596e1de9d10", +"postgresql": "2166772bfafca596e1de9d10", +"snowflake": "2166772bfafca596e1de9d10", +"tsql": "2166772bfafca596e1de9d10" +} +], +[ +"type", +{ +"DuckDB": "8441d00030df06ad6a5534d3", +"None": "8441d00030df06ad6a5534d3", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "8441d00030df06ad6a5534d3", +"duckdb": "8441d00030df06ad6a5534d3", +"generic": "8441d00030df06ad6a5534d3", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8441d00030df06ad6a5534d3", +"postgres": "8441d00030df06ad6a5534d3", +"postgresql": "8441d00030df06ad6a5534d3", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "8441d00030df06ad6a5534d3" +} +], +[ +"type", +{ +"DuckDB": "013a8446f043affffcf194f8", +"None": "013a8446f043affffcf194f8", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "013a8446f043affffcf194f8", +"duckdb": "013a8446f043affffcf194f8", +"generic": "013a8446f043affffcf194f8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "013a8446f043affffcf194f8", +"postgres": "013a8446f043affffcf194f8", +"postgresql": "013a8446f043affffcf194f8", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "013a8446f043affffcf194f8" +} +], +[ +"type", +{ +"DuckDB": "461923a0d34defd0c726d6e5", +"None": "461923a0d34defd0c726d6e5", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "461923a0d34defd0c726d6e5", +"duckdb": "461923a0d34defd0c726d6e5", +"generic": "461923a0d34defd0c726d6e5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "461923a0d34defd0c726d6e5", +"postgres": "461923a0d34defd0c726d6e5", +"postgresql": "461923a0d34defd0c726d6e5", +"snowflake": "461923a0d34defd0c726d6e5", +"tsql": "461923a0d34defd0c726d6e5" +} +], +[ +"type", +{ +"DuckDB": "dcbfb83ead5b0e32e500635b", +"None": "dcbfb83ead5b0e32e500635b", +"bigquery": "dcbfb83ead5b0e32e500635b", +"databricks": "dcbfb83ead5b0e32e500635b", +"duckdb": "dcbfb83ead5b0e32e500635b", +"generic": "dcbfb83ead5b0e32e500635b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dcbfb83ead5b0e32e500635b", +"postgres": "dcbfb83ead5b0e32e500635b", +"postgresql": "dcbfb83ead5b0e32e500635b", +"snowflake": "dcbfb83ead5b0e32e500635b", +"tsql": "dcbfb83ead5b0e32e500635b" +} +], +[ +"type", +{ +"DuckDB": "a686bb6db17ab079cfc79224", +"None": "a686bb6db17ab079cfc79224", +"bigquery": "a686bb6db17ab079cfc79224", +"databricks": "a686bb6db17ab079cfc79224", +"duckdb": "a686bb6db17ab079cfc79224", +"generic": "a686bb6db17ab079cfc79224", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a686bb6db17ab079cfc79224", +"postgres": "a686bb6db17ab079cfc79224", +"postgresql": "a686bb6db17ab079cfc79224", +"snowflake": "1096c3c8687c8e8eb538ff74", +"tsql": "a686bb6db17ab079cfc79224" +} +], +[ +"type", +{ +"DuckDB": "9d006c5299feb04426ce31ac", +"None": "e2879cf4770e7f48d2dd704a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "e2879cf4770e7f48d2dd704a", +"duckdb": "9d006c5299feb04426ce31ac", +"generic": "e2879cf4770e7f48d2dd704a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2879cf4770e7f48d2dd704a", +"postgres": "e2879cf4770e7f48d2dd704a", +"postgresql": "e2879cf4770e7f48d2dd704a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "e2879cf4770e7f48d2dd704a" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "013a8446f043affffcf194f8", +"None": "013a8446f043affffcf194f8", +"bigquery": "013a8446f043affffcf194f8", +"databricks": "013a8446f043affffcf194f8", +"duckdb": "013a8446f043affffcf194f8", +"generic": "013a8446f043affffcf194f8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "013a8446f043affffcf194f8", +"postgres": "013a8446f043affffcf194f8", +"postgresql": "013a8446f043affffcf194f8", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "013a8446f043affffcf194f8" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "8790273499dae4e6cbd2d034", +"None": "8790273499dae4e6cbd2d034", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "8790273499dae4e6cbd2d034", +"duckdb": "8790273499dae4e6cbd2d034", +"generic": "8790273499dae4e6cbd2d034", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8790273499dae4e6cbd2d034", +"postgres": "8790273499dae4e6cbd2d034", +"postgresql": "8790273499dae4e6cbd2d034", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "8790273499dae4e6cbd2d034" +} +], +[ +"type", +{ +"DuckDB": "19e127c23b4598727a2d8ceb", +"None": "19e127c23b4598727a2d8ceb", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "19e127c23b4598727a2d8ceb", +"generic": "19e127c23b4598727a2d8ceb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aed0419e7f2ce7c0a8892248", +"postgres": "19e127c23b4598727a2d8ceb", +"postgresql": "19e127c23b4598727a2d8ceb", +"snowflake": "aed0419e7f2ce7c0a8892248", +"tsql": "19e127c23b4598727a2d8ceb" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "bce60580ef3a1dc4ce569fff", +"None": "bce60580ef3a1dc4ce569fff", +"bigquery": "bce60580ef3a1dc4ce569fff", +"databricks": "bce60580ef3a1dc4ce569fff", +"duckdb": "bce60580ef3a1dc4ce569fff", +"generic": "bce60580ef3a1dc4ce569fff", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bce60580ef3a1dc4ce569fff", +"postgres": "bce60580ef3a1dc4ce569fff", +"postgresql": "bce60580ef3a1dc4ce569fff", +"snowflake": "bce60580ef3a1dc4ce569fff", +"tsql": "bce60580ef3a1dc4ce569fff" +} +], +[ +"type", +{ +"DuckDB": "305aaf0b95cee629dd46380c", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "305aaf0b95cee629dd46380c", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "73ed909ac0c80ba48ae6d17c", +"None": "73ed909ac0c80ba48ae6d17c", +"bigquery": "73ed909ac0c80ba48ae6d17c", +"databricks": "73ed909ac0c80ba48ae6d17c", +"duckdb": "73ed909ac0c80ba48ae6d17c", +"generic": "73ed909ac0c80ba48ae6d17c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73ed909ac0c80ba48ae6d17c", +"postgres": "73ed909ac0c80ba48ae6d17c", +"postgresql": "73ed909ac0c80ba48ae6d17c", +"snowflake": "73ed909ac0c80ba48ae6d17c", +"tsql": "73ed909ac0c80ba48ae6d17c" +} +], +[ +"type", +{ +"DuckDB": "c6654395c62d21b415299f72", +"None": "c6654395c62d21b415299f72", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "c6654395c62d21b415299f72", +"duckdb": "c6654395c62d21b415299f72", +"generic": "c6654395c62d21b415299f72", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2978a8fe925c7f93c6e9fcfb", +"postgres": "c6654395c62d21b415299f72", +"postgresql": "c6654395c62d21b415299f72", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "c6654395c62d21b415299f72" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "53100c87dc0e46dc31d2c6cc", +"None": "53100c87dc0e46dc31d2c6cc", +"bigquery": "53100c87dc0e46dc31d2c6cc", +"databricks": "53100c87dc0e46dc31d2c6cc", +"duckdb": "53100c87dc0e46dc31d2c6cc", +"generic": "53100c87dc0e46dc31d2c6cc", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "53100c87dc0e46dc31d2c6cc", +"postgres": "53100c87dc0e46dc31d2c6cc", +"postgresql": "53100c87dc0e46dc31d2c6cc", +"snowflake": "53100c87dc0e46dc31d2c6cc", +"tsql": "53100c87dc0e46dc31d2c6cc" +} +], +[ +"type", +{ +"DuckDB": "081a4ec9ca8c1ffbb8c41e4d", +"None": "6992a9c8899f9fe5de26869c", +"bigquery": "6992a9c8899f9fe5de26869c", +"databricks": "6992a9c8899f9fe5de26869c", +"duckdb": "081a4ec9ca8c1ffbb8c41e4d", +"generic": "6992a9c8899f9fe5de26869c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "081a4ec9ca8c1ffbb8c41e4d", +"postgresql": "081a4ec9ca8c1ffbb8c41e4d", +"snowflake": "808e51c2deac58e29ecb851e", +"tsql": "8aa3dfa0d32e1cbc68a6e4d3" +} +], +[ +"type", +{ +"DuckDB": "0c4610f182f18bf41d306e01", +"None": "0c4610f182f18bf41d306e01", +"bigquery": "0c4610f182f18bf41d306e01", +"databricks": "0c4610f182f18bf41d306e01", +"duckdb": "0c4610f182f18bf41d306e01", +"generic": "0c4610f182f18bf41d306e01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c4610f182f18bf41d306e01", +"postgres": "0c4610f182f18bf41d306e01", +"postgresql": "0c4610f182f18bf41d306e01", +"snowflake": "0c4610f182f18bf41d306e01", +"tsql": "0c4610f182f18bf41d306e01" +} +], +[ +"type", +{ +"DuckDB": "e068bc199d71601c823bb254", +"None": "e068bc199d71601c823bb254", +"bigquery": "e068bc199d71601c823bb254", +"databricks": "e068bc199d71601c823bb254", +"duckdb": "e068bc199d71601c823bb254", +"generic": "e068bc199d71601c823bb254", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e068bc199d71601c823bb254", +"postgres": "e068bc199d71601c823bb254", +"postgresql": "e068bc199d71601c823bb254", +"snowflake": "e068bc199d71601c823bb254", +"tsql": "e068bc199d71601c823bb254" +} +], +[ +"type", +{ +"DuckDB": "18036ada4276b0930d870788", +"None": "18036ada4276b0930d870788", +"bigquery": "18036ada4276b0930d870788", +"databricks": "18036ada4276b0930d870788", +"duckdb": "18036ada4276b0930d870788", +"generic": "18036ada4276b0930d870788", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "18036ada4276b0930d870788", +"postgres": "18036ada4276b0930d870788", +"postgresql": "18036ada4276b0930d870788", +"snowflake": "18036ada4276b0930d870788", +"tsql": "18036ada4276b0930d870788" +} +], +[ +"type", +{ +"DuckDB": "3b86998e2968bafafb02d6c0", +"None": "2f6a1345f274f18462c576a9", +"bigquery": "1cf06c90ca1d122a72b1be9f", +"databricks": "360298d6139a1e6f476862de", +"duckdb": "3b86998e2968bafafb02d6c0", +"generic": "2f6a1345f274f18462c576a9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "3b86998e2968bafafb02d6c0", +"postgresql": "3b86998e2968bafafb02d6c0", +"snowflake": "f5272904633fd6945c70dc00", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "c6889f2965db73017be6f6e7", +"None": "c6889f2965db73017be6f6e7", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "c6889f2965db73017be6f6e7", +"duckdb": "c6889f2965db73017be6f6e7", +"generic": "c6889f2965db73017be6f6e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c6889f2965db73017be6f6e7", +"postgres": "c6889f2965db73017be6f6e7", +"postgresql": "c6889f2965db73017be6f6e7", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c6889f2965db73017be6f6e7" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "93fcb7d3c2979f1531daccf5", +"None": "93fcb7d3c2979f1531daccf5", +"bigquery": "93fcb7d3c2979f1531daccf5", +"databricks": "93fcb7d3c2979f1531daccf5", +"duckdb": "93fcb7d3c2979f1531daccf5", +"generic": "93fcb7d3c2979f1531daccf5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "93fcb7d3c2979f1531daccf5", +"postgres": "93fcb7d3c2979f1531daccf5", +"postgresql": "93fcb7d3c2979f1531daccf5", +"snowflake": "93fcb7d3c2979f1531daccf5", +"tsql": "93fcb7d3c2979f1531daccf5" +} +], +[ +"type", +{ +"DuckDB": "0475b57e9e9da81e87fa09ba", +"None": "0475b57e9e9da81e87fa09ba", +"bigquery": "0475b57e9e9da81e87fa09ba", +"databricks": "0475b57e9e9da81e87fa09ba", +"duckdb": "0475b57e9e9da81e87fa09ba", +"generic": "0475b57e9e9da81e87fa09ba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0475b57e9e9da81e87fa09ba", +"postgres": "0475b57e9e9da81e87fa09ba", +"postgresql": "0475b57e9e9da81e87fa09ba", +"snowflake": "0475b57e9e9da81e87fa09ba", +"tsql": "0475b57e9e9da81e87fa09ba" +} +], +[ +"type", +{ +"DuckDB": "f35059f8984f0ac12ad1dc75", +"None": "f35059f8984f0ac12ad1dc75", +"bigquery": "f35059f8984f0ac12ad1dc75", +"databricks": "f35059f8984f0ac12ad1dc75", +"duckdb": "f35059f8984f0ac12ad1dc75", +"generic": "f35059f8984f0ac12ad1dc75", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f35059f8984f0ac12ad1dc75", +"postgres": "f35059f8984f0ac12ad1dc75", +"postgresql": "f35059f8984f0ac12ad1dc75", +"snowflake": "f35059f8984f0ac12ad1dc75", +"tsql": "f35059f8984f0ac12ad1dc75" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "46487f079f1382836c406de7", +"bigquery": "46487f079f1382836c406de7", +"databricks": "46487f079f1382836c406de7", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "46487f079f1382836c406de7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "f43dac38f34a1c0618a3f812", +"None": "f43dac38f34a1c0618a3f812", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "f43dac38f34a1c0618a3f812", +"duckdb": "f43dac38f34a1c0618a3f812", +"generic": "f43dac38f34a1c0618a3f812", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f43dac38f34a1c0618a3f812", +"postgres": "f43dac38f34a1c0618a3f812", +"postgresql": "f43dac38f34a1c0618a3f812", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "f43dac38f34a1c0618a3f812" +} +], +[ +"type", +{ +"DuckDB": "42a76d5ff05d0f91911ff368", +"None": "42a76d5ff05d0f91911ff368", +"bigquery": "42a76d5ff05d0f91911ff368", +"databricks": "42a76d5ff05d0f91911ff368", +"duckdb": "42a76d5ff05d0f91911ff368", +"generic": "42a76d5ff05d0f91911ff368", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "42a76d5ff05d0f91911ff368", +"postgres": "42a76d5ff05d0f91911ff368", +"postgresql": "42a76d5ff05d0f91911ff368", +"snowflake": "42a76d5ff05d0f91911ff368", +"tsql": "42a76d5ff05d0f91911ff368" +} +], +[ +"type", +{ +"DuckDB": "7cd31289a5b99b5a5e24492d", +"None": "7cd31289a5b99b5a5e24492d", +"bigquery": "7cd31289a5b99b5a5e24492d", +"databricks": "7cd31289a5b99b5a5e24492d", +"duckdb": "7cd31289a5b99b5a5e24492d", +"generic": "7cd31289a5b99b5a5e24492d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7cd31289a5b99b5a5e24492d", +"postgres": "7cd31289a5b99b5a5e24492d", +"postgresql": "7cd31289a5b99b5a5e24492d", +"snowflake": "7cd31289a5b99b5a5e24492d", +"tsql": "7cd31289a5b99b5a5e24492d" +} +], +[ +"type", +{ +"DuckDB": "cbd04e906c540e91d7c8ea88", +"None": "cbd04e906c540e91d7c8ea88", +"bigquery": "b5c4d5044b2a6e70633f0d0a", +"databricks": "cbd04e906c540e91d7c8ea88", +"duckdb": "cbd04e906c540e91d7c8ea88", +"generic": "cbd04e906c540e91d7c8ea88", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cbd04e906c540e91d7c8ea88", +"postgres": "cbd04e906c540e91d7c8ea88", +"postgresql": "cbd04e906c540e91d7c8ea88", +"snowflake": "cbd04e906c540e91d7c8ea88", +"tsql": "cbd04e906c540e91d7c8ea88" +} +], +[ +"type", +{ +"DuckDB": "7177019d5faf5b3046623257", +"None": "7177019d5faf5b3046623257", +"bigquery": "7177019d5faf5b3046623257", +"databricks": "7177019d5faf5b3046623257", +"duckdb": "7177019d5faf5b3046623257", +"generic": "7177019d5faf5b3046623257", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7177019d5faf5b3046623257", +"postgres": "7177019d5faf5b3046623257", +"postgresql": "7177019d5faf5b3046623257", +"snowflake": "7177019d5faf5b3046623257", +"tsql": "7177019d5faf5b3046623257" +} +], +[ +"type", +{ +"DuckDB": "277693b7c19b2696f01403ba", +"None": "277693b7c19b2696f01403ba", +"bigquery": "277693b7c19b2696f01403ba", +"databricks": "277693b7c19b2696f01403ba", +"duckdb": "277693b7c19b2696f01403ba", +"generic": "277693b7c19b2696f01403ba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "277693b7c19b2696f01403ba", +"postgres": "277693b7c19b2696f01403ba", +"postgresql": "277693b7c19b2696f01403ba", +"snowflake": "277693b7c19b2696f01403ba", +"tsql": "277693b7c19b2696f01403ba" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "72bd5e38b32893397eb51579", +"None": "730d7cf7510ba0934828d971", +"bigquery": "730d7cf7510ba0934828d971", +"databricks": "730d7cf7510ba0934828d971", +"duckdb": "72bd5e38b32893397eb51579", +"generic": "730d7cf7510ba0934828d971", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1bdf5908ca249b2331a0427c", +"postgres": "1bdf5908ca249b2331a0427c", +"postgresql": "1bdf5908ca249b2331a0427c", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "1bdf5908ca249b2331a0427c" +} +], +[ +"type", +{ +"DuckDB": "e1c74d96ac578f9323bf1bbd", +"None": "e1c74d96ac578f9323bf1bbd", +"bigquery": "e1c74d96ac578f9323bf1bbd", +"databricks": "e1c74d96ac578f9323bf1bbd", +"duckdb": "e1c74d96ac578f9323bf1bbd", +"generic": "e1c74d96ac578f9323bf1bbd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e1c74d96ac578f9323bf1bbd", +"postgres": "e1c74d96ac578f9323bf1bbd", +"postgresql": "e1c74d96ac578f9323bf1bbd", +"snowflake": "e1c74d96ac578f9323bf1bbd", +"tsql": "e1c74d96ac578f9323bf1bbd" +} +], +[ +"type", +{ +"DuckDB": "cafb5cff5526dd419baad0ed", +"None": "cafb5cff5526dd419baad0ed", +"bigquery": "cafb5cff5526dd419baad0ed", +"databricks": "cafb5cff5526dd419baad0ed", +"duckdb": "cafb5cff5526dd419baad0ed", +"generic": "cafb5cff5526dd419baad0ed", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cafb5cff5526dd419baad0ed", +"postgres": "cafb5cff5526dd419baad0ed", +"postgresql": "cafb5cff5526dd419baad0ed", +"snowflake": "cafb5cff5526dd419baad0ed", +"tsql": "cafb5cff5526dd419baad0ed" +} +], +[ +"type", +{ +"DuckDB": "e8b6e0bd2ded7b9eec6209e4", +"None": "e8b6e0bd2ded7b9eec6209e4", +"bigquery": "e8b6e0bd2ded7b9eec6209e4", +"databricks": "e8b6e0bd2ded7b9eec6209e4", +"duckdb": "e8b6e0bd2ded7b9eec6209e4", +"generic": "e8b6e0bd2ded7b9eec6209e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8b6e0bd2ded7b9eec6209e4", +"postgres": "e8b6e0bd2ded7b9eec6209e4", +"postgresql": "e8b6e0bd2ded7b9eec6209e4", +"snowflake": "e8b6e0bd2ded7b9eec6209e4", +"tsql": "e8b6e0bd2ded7b9eec6209e4" +} +], +[ +"type", +{ +"DuckDB": "b2c9b6ebca6c097f3caa0aa8", +"None": "b2c9b6ebca6c097f3caa0aa8", +"bigquery": "b2c9b6ebca6c097f3caa0aa8", +"databricks": "b2c9b6ebca6c097f3caa0aa8", +"duckdb": "b2c9b6ebca6c097f3caa0aa8", +"generic": "b2c9b6ebca6c097f3caa0aa8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b2c9b6ebca6c097f3caa0aa8", +"postgres": "b2c9b6ebca6c097f3caa0aa8", +"postgresql": "b2c9b6ebca6c097f3caa0aa8", +"snowflake": "b2c9b6ebca6c097f3caa0aa8", +"tsql": "b2c9b6ebca6c097f3caa0aa8" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "18db5afe2c1e80d1ee652b54", +"None": "18db5afe2c1e80d1ee652b54", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "18db5afe2c1e80d1ee652b54", +"duckdb": "18db5afe2c1e80d1ee652b54", +"generic": "18db5afe2c1e80d1ee652b54", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "18db5afe2c1e80d1ee652b54", +"postgres": "18db5afe2c1e80d1ee652b54", +"postgresql": "18db5afe2c1e80d1ee652b54", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "18db5afe2c1e80d1ee652b54" +} +], +[ +"type", +{ +"DuckDB": "219314eaf94cabfe1c7e559c", +"None": "4aab4bdd20039d5e16058b1b", +"bigquery": "5524945ebcb15c363d317b3c", +"databricks": "5524945ebcb15c363d317b3c", +"duckdb": "219314eaf94cabfe1c7e559c", +"generic": "4aab4bdd20039d5e16058b1b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5524945ebcb15c363d317b3c", +"postgres": "219314eaf94cabfe1c7e559c", +"postgresql": "219314eaf94cabfe1c7e559c", +"snowflake": "b767a0334d4c0090a22665f5", +"tsql": "5524945ebcb15c363d317b3c" +} +], +[ +"type", +{ +"DuckDB": "6a2162b5abe5a73734c47041", +"None": "6a2162b5abe5a73734c47041", +"bigquery": "266b57bc7351f5093413db07", +"databricks": "84e86bec7646b6ad19ceef59", +"duckdb": "6a2162b5abe5a73734c47041", +"generic": "6a2162b5abe5a73734c47041", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a2162b5abe5a73734c47041", +"postgres": "6a2162b5abe5a73734c47041", +"postgresql": "6a2162b5abe5a73734c47041", +"snowflake": "16d902e899040d0c0c9dcf58", +"tsql": "6a2162b5abe5a73734c47041" +} +], +[ +"type", +{ +"DuckDB": "8790273499dae4e6cbd2d034", +"None": "8790273499dae4e6cbd2d034", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "8790273499dae4e6cbd2d034", +"duckdb": "8790273499dae4e6cbd2d034", +"generic": "8790273499dae4e6cbd2d034", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8790273499dae4e6cbd2d034", +"postgres": "8790273499dae4e6cbd2d034", +"postgresql": "8790273499dae4e6cbd2d034", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "8790273499dae4e6cbd2d034" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "bf74994a132713cd5a93b70b", +"None": "74d4939af66c472f17ecfa08", +"bigquery": "5f4c9cd249f4322bf4d4cfef", +"databricks": "77298ddff3580b41e361ba6d", +"duckdb": "bf74994a132713cd5a93b70b", +"generic": "74d4939af66c472f17ecfa08", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ce873d09a353281e316461aa", +"postgres": "cf182821f911fadedb1f868d", +"postgresql": "cf182821f911fadedb1f868d", +"snowflake": "5c5b258591d9f7cb305a4220", +"tsql": "5dab25dee7ce9aba4c71c811" +} +], +[ +"type", +{ +"DuckDB": "b66ef401bfc62ea5f67697f0", +"None": "b66ef401bfc62ea5f67697f0", +"bigquery": "b66ef401bfc62ea5f67697f0", +"databricks": "b66ef401bfc62ea5f67697f0", +"duckdb": "b66ef401bfc62ea5f67697f0", +"generic": "b66ef401bfc62ea5f67697f0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b66ef401bfc62ea5f67697f0", +"postgres": "b66ef401bfc62ea5f67697f0", +"postgresql": "b66ef401bfc62ea5f67697f0", +"snowflake": "b66ef401bfc62ea5f67697f0", +"tsql": "b66ef401bfc62ea5f67697f0" +} +], +[ +"type", +{ +"DuckDB": "8203bd5901a8ec71ff7c103e", +"None": "8203bd5901a8ec71ff7c103e", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "8203bd5901a8ec71ff7c103e", +"duckdb": "8203bd5901a8ec71ff7c103e", +"generic": "8203bd5901a8ec71ff7c103e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8203bd5901a8ec71ff7c103e", +"postgres": "8203bd5901a8ec71ff7c103e", +"postgresql": "8203bd5901a8ec71ff7c103e", +"snowflake": "8203bd5901a8ec71ff7c103e", +"tsql": "8203bd5901a8ec71ff7c103e" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "6199046833cb1b5de6e50c1f", +"None": "6199046833cb1b5de6e50c1f", +"bigquery": "6199046833cb1b5de6e50c1f", +"databricks": "6199046833cb1b5de6e50c1f", +"duckdb": "6199046833cb1b5de6e50c1f", +"generic": "6199046833cb1b5de6e50c1f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6199046833cb1b5de6e50c1f", +"postgres": "6199046833cb1b5de6e50c1f", +"postgresql": "6199046833cb1b5de6e50c1f", +"snowflake": "6199046833cb1b5de6e50c1f", +"tsql": "6199046833cb1b5de6e50c1f" +} +], +[ +"type", +{ +"DuckDB": "f1158467f7ab7b47d7e993d0", +"None": "f1158467f7ab7b47d7e993d0", +"bigquery": "f1158467f7ab7b47d7e993d0", +"databricks": "f1158467f7ab7b47d7e993d0", +"duckdb": "f1158467f7ab7b47d7e993d0", +"generic": "f1158467f7ab7b47d7e993d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f1158467f7ab7b47d7e993d0", +"postgres": "f1158467f7ab7b47d7e993d0", +"postgresql": "f1158467f7ab7b47d7e993d0", +"snowflake": "f1158467f7ab7b47d7e993d0", +"tsql": "f1158467f7ab7b47d7e993d0" +} +], +[ +"type", +{ +"DuckDB": "23f70cb2333740d79361d52e", +"None": "23f70cb2333740d79361d52e", +"bigquery": "816384049477221b0a4a5b89", +"databricks": "23f70cb2333740d79361d52e", +"duckdb": "23f70cb2333740d79361d52e", +"generic": "23f70cb2333740d79361d52e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23f70cb2333740d79361d52e", +"postgres": "23f70cb2333740d79361d52e", +"postgresql": "23f70cb2333740d79361d52e", +"snowflake": "23f70cb2333740d79361d52e", +"tsql": "23f70cb2333740d79361d52e" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "527932781ba4bab32cb5e881", +"None": "527932781ba4bab32cb5e881", +"bigquery": "527932781ba4bab32cb5e881", +"databricks": "527932781ba4bab32cb5e881", +"duckdb": "527932781ba4bab32cb5e881", +"generic": "527932781ba4bab32cb5e881", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "527932781ba4bab32cb5e881", +"postgres": "527932781ba4bab32cb5e881", +"postgresql": "527932781ba4bab32cb5e881", +"snowflake": "527932781ba4bab32cb5e881", +"tsql": "527932781ba4bab32cb5e881" +} +], +[ +"type", +{ +"DuckDB": "623dbde002b57284f477b0b5", +"None": "623dbde002b57284f477b0b5", +"bigquery": "623dbde002b57284f477b0b5", +"databricks": "623dbde002b57284f477b0b5", +"duckdb": "623dbde002b57284f477b0b5", +"generic": "623dbde002b57284f477b0b5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "623dbde002b57284f477b0b5", +"postgres": "623dbde002b57284f477b0b5", +"postgresql": "623dbde002b57284f477b0b5", +"snowflake": "623dbde002b57284f477b0b5", +"tsql": "623dbde002b57284f477b0b5" +} +], +[ +"type", +{ +"DuckDB": "a12a2ad8c11bbf6a41794d33", +"None": "a12a2ad8c11bbf6a41794d33", +"bigquery": "a12a2ad8c11bbf6a41794d33", +"databricks": "a12a2ad8c11bbf6a41794d33", +"duckdb": "a12a2ad8c11bbf6a41794d33", +"generic": "a12a2ad8c11bbf6a41794d33", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a12a2ad8c11bbf6a41794d33", +"postgres": "a12a2ad8c11bbf6a41794d33", +"postgresql": "a12a2ad8c11bbf6a41794d33", +"snowflake": "a12a2ad8c11bbf6a41794d33", +"tsql": "a12a2ad8c11bbf6a41794d33" +} +], +[ +"type", +{ +"DuckDB": "558da38ec4d9d5d6c72f60cf", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "558da38ec4d9d5d6c72f60cf", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "363159f251e264604338e690", +"None": "363159f251e264604338e690", +"bigquery": "363159f251e264604338e690", +"databricks": "363159f251e264604338e690", +"duckdb": "363159f251e264604338e690", +"generic": "363159f251e264604338e690", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "363159f251e264604338e690", +"postgres": "363159f251e264604338e690", +"postgresql": "363159f251e264604338e690", +"snowflake": "363159f251e264604338e690", +"tsql": "363159f251e264604338e690" +} +], +[ +"type", +{ +"DuckDB": "b6200c25bc3749f5724d237c", +"None": "b6200c25bc3749f5724d237c", +"bigquery": "b6200c25bc3749f5724d237c", +"databricks": "6b1db1e2f94caeb40dbea19b", +"duckdb": "b6200c25bc3749f5724d237c", +"generic": "b6200c25bc3749f5724d237c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "b6200c25bc3749f5724d237c", +"postgresql": "b6200c25bc3749f5724d237c", +"snowflake": "b6200c25bc3749f5724d237c", +"tsql": "b6200c25bc3749f5724d237c" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "129cd0245509c52e0d7ac815", +"None": "8d1119d9033bc544ced02739", +"bigquery": "ef3ab21b1ec5acc43562f6e2", +"databricks": "5b41dbff68938ce2e8a5c35d", +"duckdb": "129cd0245509c52e0d7ac815", +"generic": "8d1119d9033bc544ced02739", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b9f1a78bcda3bc968346cc10", +"postgres": "129cd0245509c52e0d7ac815", +"postgresql": "129cd0245509c52e0d7ac815", +"snowflake": "24b68111659c8d395da7df6a", +"tsql": "e42dc1c3607e8e3b3c865c3f" +} +], +[ +"type", +{ +"DuckDB": "60bd7daa04bed898e52118ab", +"None": "070c0836fb3402c924b00b0e", +"bigquery": "3aac0f59706540c7a59cbce4", +"databricks": "070c0836fb3402c924b00b0e", +"duckdb": "60bd7daa04bed898e52118ab", +"generic": "070c0836fb3402c924b00b0e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "60bd7daa04bed898e52118ab", +"postgresql": "60bd7daa04bed898e52118ab", +"snowflake": "0a5fcaec13e26e3caacd5345", +"tsql": "33f30462ac4f41af4b28bd1a" +} +], +[ +"type", +{ +"DuckDB": "2f6203391b0efaaf0a3c9d00", +"None": "2f6203391b0efaaf0a3c9d00", +"bigquery": "23266be42b7761d5b86b200a", +"databricks": "94006e4eb73bd1f728de7a83", +"duckdb": "2f6203391b0efaaf0a3c9d00", +"generic": "2f6203391b0efaaf0a3c9d00", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2f6203391b0efaaf0a3c9d00", +"postgres": "2f6203391b0efaaf0a3c9d00", +"postgresql": "2f6203391b0efaaf0a3c9d00", +"snowflake": "dc61037c72b2e7b4730d26cc", +"tsql": "2f6203391b0efaaf0a3c9d00" +} +], +[ +"type", +{ +"DuckDB": "6a534f7a641457b40e35d984", +"None": "6a534f7a641457b40e35d984", +"bigquery": "6a534f7a641457b40e35d984", +"databricks": "6a534f7a641457b40e35d984", +"duckdb": "6a534f7a641457b40e35d984", +"generic": "6a534f7a641457b40e35d984", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6a534f7a641457b40e35d984", +"postgres": "6a534f7a641457b40e35d984", +"postgresql": "6a534f7a641457b40e35d984", +"snowflake": "6a534f7a641457b40e35d984", +"tsql": "6a534f7a641457b40e35d984" +} +], +[ +"type", +{ +"DuckDB": "60b39a68a18438603dd5496d", +"None": "60b39a68a18438603dd5496d", +"bigquery": "60b39a68a18438603dd5496d", +"databricks": "60b39a68a18438603dd5496d", +"duckdb": "60b39a68a18438603dd5496d", +"generic": "60b39a68a18438603dd5496d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "60b39a68a18438603dd5496d", +"postgres": "60b39a68a18438603dd5496d", +"postgresql": "60b39a68a18438603dd5496d", +"snowflake": "60b39a68a18438603dd5496d", +"tsql": "60b39a68a18438603dd5496d" +} +], +[ +"type", +{ +"DuckDB": "126765943545b2608ebe7128", +"None": "126765943545b2608ebe7128", +"bigquery": "126765943545b2608ebe7128", +"databricks": "126765943545b2608ebe7128", +"duckdb": "126765943545b2608ebe7128", +"generic": "126765943545b2608ebe7128", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "126765943545b2608ebe7128", +"postgres": "126765943545b2608ebe7128", +"postgresql": "126765943545b2608ebe7128", +"snowflake": "126765943545b2608ebe7128", +"tsql": "126765943545b2608ebe7128" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "9c2cff8be7225cf79883ca65", +"None": "9c2cff8be7225cf79883ca65", +"bigquery": "9c2cff8be7225cf79883ca65", +"databricks": "901ac89f8d9ec18bd3a61e4b", +"duckdb": "9c2cff8be7225cf79883ca65", +"generic": "9c2cff8be7225cf79883ca65", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "901ac89f8d9ec18bd3a61e4b", +"postgres": "9c2cff8be7225cf79883ca65", +"postgresql": "9c2cff8be7225cf79883ca65", +"snowflake": "9c2cff8be7225cf79883ca65", +"tsql": "9c2cff8be7225cf79883ca65" +} +], +[ +"type", +{ +"DuckDB": "cf05d378f282d0abe941a82f", +"None": "cf05d378f282d0abe941a82f", +"bigquery": "cf05d378f282d0abe941a82f", +"databricks": "cf05d378f282d0abe941a82f", +"duckdb": "cf05d378f282d0abe941a82f", +"generic": "cf05d378f282d0abe941a82f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cf05d378f282d0abe941a82f", +"postgres": "cf05d378f282d0abe941a82f", +"postgresql": "cf05d378f282d0abe941a82f", +"snowflake": "cf05d378f282d0abe941a82f", +"tsql": "cf05d378f282d0abe941a82f" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "d7b74701deb8b87c32deb7a6", +"None": "d7b74701deb8b87c32deb7a6", +"bigquery": "d7b74701deb8b87c32deb7a6", +"databricks": "d7b74701deb8b87c32deb7a6", +"duckdb": "d7b74701deb8b87c32deb7a6", +"generic": "d7b74701deb8b87c32deb7a6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d7b74701deb8b87c32deb7a6", +"postgres": "d7b74701deb8b87c32deb7a6", +"postgresql": "d7b74701deb8b87c32deb7a6", +"snowflake": "d7b74701deb8b87c32deb7a6", +"tsql": "d7b74701deb8b87c32deb7a6" +} +], +[ +"type", +{ +"DuckDB": "078c0e4b25cdb89eb4ebd88f", +"None": "078c0e4b25cdb89eb4ebd88f", +"bigquery": "5226d7e2a62c7650a87d6e84", +"databricks": "078c0e4b25cdb89eb4ebd88f", +"duckdb": "078c0e4b25cdb89eb4ebd88f", +"generic": "078c0e4b25cdb89eb4ebd88f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "078c0e4b25cdb89eb4ebd88f", +"postgres": "078c0e4b25cdb89eb4ebd88f", +"postgresql": "078c0e4b25cdb89eb4ebd88f", +"snowflake": "6306b9e88f7b13dbb6a32c00", +"tsql": "078c0e4b25cdb89eb4ebd88f" +} +], +[ +"type", +{ +"DuckDB": "e46d4be44ab9e500e9190d26", +"None": "e46d4be44ab9e500e9190d26", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "e46d4be44ab9e500e9190d26", +"duckdb": "e46d4be44ab9e500e9190d26", +"generic": "e46d4be44ab9e500e9190d26", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e46d4be44ab9e500e9190d26", +"postgres": "e46d4be44ab9e500e9190d26", +"postgresql": "e46d4be44ab9e500e9190d26", +"snowflake": "e46d4be44ab9e500e9190d26", +"tsql": "e46d4be44ab9e500e9190d26" +} +], +[ +"type", +{ +"DuckDB": "dcd0570bcd1ca6e7cbe9b206", +"None": "dcd0570bcd1ca6e7cbe9b206", +"bigquery": "3b4c01f94cca3bf195a3fd00", +"databricks": "526d6b30a5f27d05c0a26aa0", +"duckdb": "dcd0570bcd1ca6e7cbe9b206", +"generic": "dcd0570bcd1ca6e7cbe9b206", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a5a929e25b7d0dae21e9bb65", +"postgres": "dcd0570bcd1ca6e7cbe9b206", +"postgresql": "dcd0570bcd1ca6e7cbe9b206", +"snowflake": "339dae99bd73c126e700c594", +"tsql": "dcd0570bcd1ca6e7cbe9b206" +} +], +[ +"type", +{ +"DuckDB": "00402a032fb29ce84ecc4615", +"None": "00402a032fb29ce84ecc4615", +"bigquery": "00402a032fb29ce84ecc4615", +"databricks": "00402a032fb29ce84ecc4615", +"duckdb": "00402a032fb29ce84ecc4615", +"generic": "00402a032fb29ce84ecc4615", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "00402a032fb29ce84ecc4615", +"postgres": "00402a032fb29ce84ecc4615", +"postgresql": "00402a032fb29ce84ecc4615", +"snowflake": "00402a032fb29ce84ecc4615", +"tsql": "00402a032fb29ce84ecc4615" +} +], +[ +"type", +{ +"DuckDB": "52921fa22fa8a5b698ef368f", +"None": "52921fa22fa8a5b698ef368f", +"bigquery": "52921fa22fa8a5b698ef368f", +"databricks": "52921fa22fa8a5b698ef368f", +"duckdb": "52921fa22fa8a5b698ef368f", +"generic": "52921fa22fa8a5b698ef368f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "52921fa22fa8a5b698ef368f", +"postgres": "52921fa22fa8a5b698ef368f", +"postgresql": "52921fa22fa8a5b698ef368f", +"snowflake": "52921fa22fa8a5b698ef368f", +"tsql": "52921fa22fa8a5b698ef368f" +} +], +[ +"type", +{ +"DuckDB": "4114b18e7e26d16613331fa7", +"None": "4114b18e7e26d16613331fa7", +"bigquery": "4114b18e7e26d16613331fa7", +"databricks": "4fb572024031cb7631777be1", +"duckdb": "4114b18e7e26d16613331fa7", +"generic": "4114b18e7e26d16613331fa7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4114b18e7e26d16613331fa7", +"postgres": "4114b18e7e26d16613331fa7", +"postgresql": "4114b18e7e26d16613331fa7", +"snowflake": "4114b18e7e26d16613331fa7", +"tsql": "4114b18e7e26d16613331fa7" +} +], +[ +"type", +{ +"DuckDB": "6226bc03436ecf8fccd39252", +"None": "5aa51d84ac5e6c7a9306f348", +"bigquery": "5aa51d84ac5e6c7a9306f348", +"databricks": "5aa51d84ac5e6c7a9306f348", +"duckdb": "6226bc03436ecf8fccd39252", +"generic": "5aa51d84ac5e6c7a9306f348", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5aa51d84ac5e6c7a9306f348", +"postgres": "5aa51d84ac5e6c7a9306f348", +"postgresql": "5aa51d84ac5e6c7a9306f348", +"snowflake": "5aa51d84ac5e6c7a9306f348", +"tsql": "5aa51d84ac5e6c7a9306f348" +} +], +[ +"type", +{ +"DuckDB": "630c8b82ec44c7208a6df405", +"None": "fa5c71c0d2354f76510bf3e7", +"bigquery": "58f6dfd7cabdc84b33dc2a96", +"databricks": "58f6dfd7cabdc84b33dc2a96", +"duckdb": "630c8b82ec44c7208a6df405", +"generic": "fa5c71c0d2354f76510bf3e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "58f6dfd7cabdc84b33dc2a96", +"postgres": "630c8b82ec44c7208a6df405", +"postgresql": "630c8b82ec44c7208a6df405", +"snowflake": "8efe0c6c8c4444b8fe68129b", +"tsql": "58f6dfd7cabdc84b33dc2a96" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "dbd808c57538b7d212ed9a7c", +"None": "dbd808c57538b7d212ed9a7c", +"bigquery": "dbd808c57538b7d212ed9a7c", +"databricks": "dbd808c57538b7d212ed9a7c", +"duckdb": "dbd808c57538b7d212ed9a7c", +"generic": "dbd808c57538b7d212ed9a7c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dbd808c57538b7d212ed9a7c", +"postgres": "dbd808c57538b7d212ed9a7c", +"postgresql": "dbd808c57538b7d212ed9a7c", +"snowflake": "dbd808c57538b7d212ed9a7c", +"tsql": "dbd808c57538b7d212ed9a7c" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "79278352c17c9de0f5a28af5", +"None": "79278352c17c9de0f5a28af5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "79278352c17c9de0f5a28af5", +"duckdb": "79278352c17c9de0f5a28af5", +"generic": "79278352c17c9de0f5a28af5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "79278352c17c9de0f5a28af5", +"postgres": "79278352c17c9de0f5a28af5", +"postgresql": "79278352c17c9de0f5a28af5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "79278352c17c9de0f5a28af5" +} +], +[ +"type", +{ +"DuckDB": "bbc7d9037d6f0383eb14d7eb", +"None": "bbc7d9037d6f0383eb14d7eb", +"bigquery": "bbc7d9037d6f0383eb14d7eb", +"databricks": "bbc7d9037d6f0383eb14d7eb", +"duckdb": "bbc7d9037d6f0383eb14d7eb", +"generic": "bbc7d9037d6f0383eb14d7eb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bbc7d9037d6f0383eb14d7eb", +"postgres": "bbc7d9037d6f0383eb14d7eb", +"postgresql": "bbc7d9037d6f0383eb14d7eb", +"snowflake": "bbc7d9037d6f0383eb14d7eb", +"tsql": "bbc7d9037d6f0383eb14d7eb" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "cb06a5553de70a4924ed6600", +"None": "cb06a5553de70a4924ed6600", +"bigquery": "cb06a5553de70a4924ed6600", +"databricks": "cb06a5553de70a4924ed6600", +"duckdb": "cb06a5553de70a4924ed6600", +"generic": "cb06a5553de70a4924ed6600", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cb06a5553de70a4924ed6600", +"postgres": "cb06a5553de70a4924ed6600", +"postgresql": "cb06a5553de70a4924ed6600", +"snowflake": "cb06a5553de70a4924ed6600", +"tsql": "cb06a5553de70a4924ed6600" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "7cd31289a5b99b5a5e24492d", +"None": "7cd31289a5b99b5a5e24492d", +"bigquery": "7cd31289a5b99b5a5e24492d", +"databricks": "7cd31289a5b99b5a5e24492d", +"duckdb": "7cd31289a5b99b5a5e24492d", +"generic": "7cd31289a5b99b5a5e24492d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7cd31289a5b99b5a5e24492d", +"postgres": "7cd31289a5b99b5a5e24492d", +"postgresql": "7cd31289a5b99b5a5e24492d", +"snowflake": "7cd31289a5b99b5a5e24492d", +"tsql": "7cd31289a5b99b5a5e24492d" +} +], +[ +"type", +{ +"DuckDB": "1eb7826ddab37fc01598a8e9", +"None": "4e7908098c87141abb62306c", +"bigquery": "4e7908098c87141abb62306c", +"databricks": "4e7908098c87141abb62306c", +"duckdb": "1eb7826ddab37fc01598a8e9", +"generic": "4e7908098c87141abb62306c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8ca050e9a43027521099ab95", +"postgresql": "8ca050e9a43027521099ab95", +"snowflake": "92f647d5460e4e2e7e25fb6d", +"tsql": "e1389f24bc9993eea233a19e" +} +], +[ +"type", +{ +"DuckDB": "a0131c4099eb4915760bab4a", +"None": "a0131c4099eb4915760bab4a", +"bigquery": "a0131c4099eb4915760bab4a", +"databricks": "a0131c4099eb4915760bab4a", +"duckdb": "a0131c4099eb4915760bab4a", +"generic": "a0131c4099eb4915760bab4a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a0131c4099eb4915760bab4a", +"postgres": "a0131c4099eb4915760bab4a", +"postgresql": "a0131c4099eb4915760bab4a", +"snowflake": "a0131c4099eb4915760bab4a", +"tsql": "a0131c4099eb4915760bab4a" +} +], +[ +"type", +{ +"DuckDB": "787213ce6b6e210ded1d752c", +"None": "787213ce6b6e210ded1d752c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "787213ce6b6e210ded1d752c", +"duckdb": "787213ce6b6e210ded1d752c", +"generic": "787213ce6b6e210ded1d752c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "787213ce6b6e210ded1d752c", +"postgres": "787213ce6b6e210ded1d752c", +"postgresql": "787213ce6b6e210ded1d752c", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "787213ce6b6e210ded1d752c" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "86b7927f577e56d61eb9f879", +"None": "86b7927f577e56d61eb9f879", +"bigquery": "86b7927f577e56d61eb9f879", +"databricks": "86b7927f577e56d61eb9f879", +"duckdb": "86b7927f577e56d61eb9f879", +"generic": "86b7927f577e56d61eb9f879", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "86b7927f577e56d61eb9f879", +"postgres": "86b7927f577e56d61eb9f879", +"postgresql": "86b7927f577e56d61eb9f879", +"snowflake": "86b7927f577e56d61eb9f879", +"tsql": "86b7927f577e56d61eb9f879" +} +], +[ +"type", +{ +"DuckDB": "c82815abe30b3d099903f838", +"None": "c82815abe30b3d099903f838", +"bigquery": "c82815abe30b3d099903f838", +"databricks": "c82815abe30b3d099903f838", +"duckdb": "c82815abe30b3d099903f838", +"generic": "c82815abe30b3d099903f838", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c82815abe30b3d099903f838", +"postgres": "c82815abe30b3d099903f838", +"postgresql": "c82815abe30b3d099903f838", +"snowflake": "c82815abe30b3d099903f838", +"tsql": "07ca53fdc57a22a78ad75787" +} +], +[ +"type", +{ +"DuckDB": "7d23077fb72f7bac9a49a632", +"None": "7d23077fb72f7bac9a49a632", +"bigquery": "7d23077fb72f7bac9a49a632", +"databricks": "7d23077fb72f7bac9a49a632", +"duckdb": "7d23077fb72f7bac9a49a632", +"generic": "7d23077fb72f7bac9a49a632", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7d23077fb72f7bac9a49a632", +"postgres": "7d23077fb72f7bac9a49a632", +"postgresql": "7d23077fb72f7bac9a49a632", +"snowflake": "7d23077fb72f7bac9a49a632", +"tsql": "7d23077fb72f7bac9a49a632" +} +], +[ +"type", +{ +"DuckDB": "177c462b3a0270e894c8111c", +"None": "177c462b3a0270e894c8111c", +"bigquery": "177c462b3a0270e894c8111c", +"databricks": "177c462b3a0270e894c8111c", +"duckdb": "177c462b3a0270e894c8111c", +"generic": "177c462b3a0270e894c8111c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "177c462b3a0270e894c8111c", +"postgres": "177c462b3a0270e894c8111c", +"postgresql": "177c462b3a0270e894c8111c", +"snowflake": "177c462b3a0270e894c8111c", +"tsql": "177c462b3a0270e894c8111c" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "8d81e8dacd544a5e07c3b1ea", +"None": "8d81e8dacd544a5e07c3b1ea", +"bigquery": "8d81e8dacd544a5e07c3b1ea", +"databricks": "8d81e8dacd544a5e07c3b1ea", +"duckdb": "8d81e8dacd544a5e07c3b1ea", +"generic": "8d81e8dacd544a5e07c3b1ea", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8d81e8dacd544a5e07c3b1ea", +"postgres": "8d81e8dacd544a5e07c3b1ea", +"postgresql": "8d81e8dacd544a5e07c3b1ea", +"snowflake": "8d81e8dacd544a5e07c3b1ea", +"tsql": "8d81e8dacd544a5e07c3b1ea" +} +], +[ +"type", +{ +"DuckDB": "2a0918be53051fe629d9ff8f", +"None": "2a0918be53051fe629d9ff8f", +"bigquery": "2a0918be53051fe629d9ff8f", +"databricks": "2a0918be53051fe629d9ff8f", +"duckdb": "2a0918be53051fe629d9ff8f", +"generic": "2a0918be53051fe629d9ff8f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2a0918be53051fe629d9ff8f", +"postgres": "2a0918be53051fe629d9ff8f", +"postgresql": "2a0918be53051fe629d9ff8f", +"snowflake": "3e611643f0beab4953c4e09b", +"tsql": "2a0918be53051fe629d9ff8f" +} +], +[ +"type", +{ +"DuckDB": "e2eee356d05f550b28bf85ad", +"None": "e2eee356d05f550b28bf85ad", +"bigquery": "e2eee356d05f550b28bf85ad", +"databricks": "e2eee356d05f550b28bf85ad", +"duckdb": "e2eee356d05f550b28bf85ad", +"generic": "e2eee356d05f550b28bf85ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e2eee356d05f550b28bf85ad", +"postgres": "e2eee356d05f550b28bf85ad", +"postgresql": "e2eee356d05f550b28bf85ad", +"snowflake": "e2eee356d05f550b28bf85ad", +"tsql": "e2eee356d05f550b28bf85ad" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "6ebf45a787a223241c8d9a47", +"None": "6ebf45a787a223241c8d9a47", +"bigquery": "6ebf45a787a223241c8d9a47", +"databricks": "6ebf45a787a223241c8d9a47", +"duckdb": "6ebf45a787a223241c8d9a47", +"generic": "6ebf45a787a223241c8d9a47", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6ebf45a787a223241c8d9a47", +"postgres": "6ebf45a787a223241c8d9a47", +"postgresql": "6ebf45a787a223241c8d9a47", +"snowflake": "6ebf45a787a223241c8d9a47", +"tsql": "6ebf45a787a223241c8d9a47" +} +], +[ +"type", +{ +"DuckDB": "cd9c38fc2cf84c8551e29278", +"None": "cd9c38fc2cf84c8551e29278", +"bigquery": "cd9c38fc2cf84c8551e29278", +"databricks": "cd9c38fc2cf84c8551e29278", +"duckdb": "cd9c38fc2cf84c8551e29278", +"generic": "cd9c38fc2cf84c8551e29278", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cd9c38fc2cf84c8551e29278", +"postgres": "cd9c38fc2cf84c8551e29278", +"postgresql": "cd9c38fc2cf84c8551e29278", +"snowflake": "cd9c38fc2cf84c8551e29278", +"tsql": "cd9c38fc2cf84c8551e29278" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "944971a819b2498d3ecdc71d", +"None": "90882506184f4f707c46fdea", +"bigquery": "f1cb74b07e7c8c7243c9034b", +"databricks": "37decbb811ce5abeffb05b4e", +"duckdb": "944971a819b2498d3ecdc71d", +"generic": "90882506184f4f707c46fdea", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "90882506184f4f707c46fdea", +"postgres": "61f2d73857de8729b66f99d2", +"postgresql": "61f2d73857de8729b66f99d2", +"snowflake": "225fc4f7b28377ba911e4a47", +"tsql": "5fb4ec49639b38cdf3ccfc38" +} +], +[ +"type", +{ +"DuckDB": "dbf9ebbc7016f5ac5df2817e", +"None": "dbf9ebbc7016f5ac5df2817e", +"bigquery": "dbf9ebbc7016f5ac5df2817e", +"databricks": "dbf9ebbc7016f5ac5df2817e", +"duckdb": "dbf9ebbc7016f5ac5df2817e", +"generic": "dbf9ebbc7016f5ac5df2817e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "dbf9ebbc7016f5ac5df2817e", +"postgres": "dbf9ebbc7016f5ac5df2817e", +"postgresql": "dbf9ebbc7016f5ac5df2817e", +"snowflake": "dbf9ebbc7016f5ac5df2817e", +"tsql": "dbf9ebbc7016f5ac5df2817e" +} +], +[ +"type", +{ +"DuckDB": "5debd5336e9bc9ec70e731b7", +"None": "5debd5336e9bc9ec70e731b7", +"bigquery": "5debd5336e9bc9ec70e731b7", +"databricks": "5debd5336e9bc9ec70e731b7", +"duckdb": "5debd5336e9bc9ec70e731b7", +"generic": "5debd5336e9bc9ec70e731b7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5debd5336e9bc9ec70e731b7", +"postgres": "5debd5336e9bc9ec70e731b7", +"postgresql": "5debd5336e9bc9ec70e731b7", +"snowflake": "5debd5336e9bc9ec70e731b7", +"tsql": "5debd5336e9bc9ec70e731b7" +} +], +[ +"type", +{ +"DuckDB": "d2c97d221599d038d9875873", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "d2c97d221599d038d9875873", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "1fdc0b9f28b115c175e88d46", +"None": "1fdc0b9f28b115c175e88d46", +"bigquery": "1fdc0b9f28b115c175e88d46", +"databricks": "1fdc0b9f28b115c175e88d46", +"duckdb": "1fdc0b9f28b115c175e88d46", +"generic": "1fdc0b9f28b115c175e88d46", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fdc0b9f28b115c175e88d46", +"postgres": "1fdc0b9f28b115c175e88d46", +"postgresql": "1fdc0b9f28b115c175e88d46", +"snowflake": "1fdc0b9f28b115c175e88d46", +"tsql": "1fdc0b9f28b115c175e88d46" +} +], +[ +"type", +{ +"DuckDB": "f38ef32a483f1c4f89ae309e", +"None": "f38ef32a483f1c4f89ae309e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f38ef32a483f1c4f89ae309e", +"duckdb": "f38ef32a483f1c4f89ae309e", +"generic": "f38ef32a483f1c4f89ae309e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f38ef32a483f1c4f89ae309e", +"postgres": "f38ef32a483f1c4f89ae309e", +"postgresql": "f38ef32a483f1c4f89ae309e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f38ef32a483f1c4f89ae309e" +} +], +[ +"type", +{ +"DuckDB": "5620e893a908c6743b863bf6", +"None": "5620e893a908c6743b863bf6", +"bigquery": "4281b54de06f589b3507035a", +"databricks": "4281b54de06f589b3507035a", +"duckdb": "5620e893a908c6743b863bf6", +"generic": "5620e893a908c6743b863bf6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4281b54de06f589b3507035a", +"postgres": "5620e893a908c6743b863bf6", +"postgresql": "5620e893a908c6743b863bf6", +"snowflake": "5620e893a908c6743b863bf6", +"tsql": "5620e893a908c6743b863bf6" +} +], +[ +"type", +{ +"DuckDB": "c0d59e65280108f563dbb04b", +"None": "c0d59e65280108f563dbb04b", +"bigquery": "c0d59e65280108f563dbb04b", +"databricks": "c0d59e65280108f563dbb04b", +"duckdb": "c0d59e65280108f563dbb04b", +"generic": "c0d59e65280108f563dbb04b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c0d59e65280108f563dbb04b", +"postgres": "c0d59e65280108f563dbb04b", +"postgresql": "c0d59e65280108f563dbb04b", +"snowflake": "c0d59e65280108f563dbb04b", +"tsql": "c0d59e65280108f563dbb04b" +} +], +[ +"type", +{ +"DuckDB": "d16d75bc8832411932952499", +"None": "d16d75bc8832411932952499", +"bigquery": "d16d75bc8832411932952499", +"databricks": "d16d75bc8832411932952499", +"duckdb": "d16d75bc8832411932952499", +"generic": "d16d75bc8832411932952499", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d16d75bc8832411932952499", +"postgres": "d16d75bc8832411932952499", +"postgresql": "d16d75bc8832411932952499", +"snowflake": "d16d75bc8832411932952499", +"tsql": "d16d75bc8832411932952499" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "6cffd1617edd6454241ee732", +"None": "6cffd1617edd6454241ee732", +"bigquery": "6cffd1617edd6454241ee732", +"databricks": "6cffd1617edd6454241ee732", +"duckdb": "6cffd1617edd6454241ee732", +"generic": "6cffd1617edd6454241ee732", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6cffd1617edd6454241ee732", +"postgres": "6cffd1617edd6454241ee732", +"postgresql": "6cffd1617edd6454241ee732", +"snowflake": "6cffd1617edd6454241ee732", +"tsql": "6cffd1617edd6454241ee732" +} +], +[ +"type", +{ +"DuckDB": "a686bb6db17ab079cfc79224", +"None": "a686bb6db17ab079cfc79224", +"bigquery": "a686bb6db17ab079cfc79224", +"databricks": "a686bb6db17ab079cfc79224", +"duckdb": "a686bb6db17ab079cfc79224", +"generic": "a686bb6db17ab079cfc79224", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a686bb6db17ab079cfc79224", +"postgres": "a686bb6db17ab079cfc79224", +"postgresql": "a686bb6db17ab079cfc79224", +"snowflake": "1096c3c8687c8e8eb538ff74", +"tsql": "a686bb6db17ab079cfc79224" +} +], +[ +"type", +{ +"DuckDB": "c752fd9fe2ad3b86b8b899da", +"None": "c752fd9fe2ad3b86b8b899da", +"bigquery": "c752fd9fe2ad3b86b8b899da", +"databricks": "c752fd9fe2ad3b86b8b899da", +"duckdb": "c752fd9fe2ad3b86b8b899da", +"generic": "c752fd9fe2ad3b86b8b899da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c752fd9fe2ad3b86b8b899da", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c752fd9fe2ad3b86b8b899da" +} +], +[ +"type", +{ +"DuckDB": "b52d5a83269c4ad64bd3416e", +"None": "b52d5a83269c4ad64bd3416e", +"bigquery": "b52d5a83269c4ad64bd3416e", +"databricks": "b52d5a83269c4ad64bd3416e", +"duckdb": "b52d5a83269c4ad64bd3416e", +"generic": "b52d5a83269c4ad64bd3416e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b52d5a83269c4ad64bd3416e", +"postgres": "b52d5a83269c4ad64bd3416e", +"postgresql": "b52d5a83269c4ad64bd3416e", +"snowflake": "b52d5a83269c4ad64bd3416e", +"tsql": "b52d5a83269c4ad64bd3416e" +} +], +[ +"type", +{ +"DuckDB": "aff2599f42411f45ff949ed9", +"None": "be47a14e7523acb3fcbd4b5b", +"bigquery": "f98c7776673323299fbfe6b7", +"databricks": "2ed848a19b2b41fdf76d9a87", +"duckdb": "aff2599f42411f45ff949ed9", +"generic": "be47a14e7523acb3fcbd4b5b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "aff2599f42411f45ff949ed9", +"postgresql": "aff2599f42411f45ff949ed9", +"snowflake": "a2a30dfc031641a60ba584fa", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "d9315b336b73c3d8d4b6e9a3", +"None": "f2ed52f6bf46b4db9343551c", +"bigquery": "f2ed52f6bf46b4db9343551c", +"databricks": "f2ed52f6bf46b4db9343551c", +"duckdb": "d9315b336b73c3d8d4b6e9a3", +"generic": "f2ed52f6bf46b4db9343551c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f2ed52f6bf46b4db9343551c", +"postgres": "f2ed52f6bf46b4db9343551c", +"postgresql": "f2ed52f6bf46b4db9343551c", +"snowflake": "f2ed52f6bf46b4db9343551c", +"tsql": "f2ed52f6bf46b4db9343551c" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "88ee4a69672b4b9b3aa21e3d", +"None": "88ee4a69672b4b9b3aa21e3d", +"bigquery": "88ee4a69672b4b9b3aa21e3d", +"databricks": "88ee4a69672b4b9b3aa21e3d", +"duckdb": "88ee4a69672b4b9b3aa21e3d", +"generic": "88ee4a69672b4b9b3aa21e3d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "88ee4a69672b4b9b3aa21e3d", +"postgres": "88ee4a69672b4b9b3aa21e3d", +"postgresql": "88ee4a69672b4b9b3aa21e3d", +"snowflake": "88ee4a69672b4b9b3aa21e3d", +"tsql": "88ee4a69672b4b9b3aa21e3d" +} +], +[ +"type", +{ +"DuckDB": "ab5ad6180652b3b00d1f5b01", +"None": "ab5ad6180652b3b00d1f5b01", +"bigquery": "946d5ee30ab7469c57d850df", +"databricks": "ab5ad6180652b3b00d1f5b01", +"duckdb": "ab5ad6180652b3b00d1f5b01", +"generic": "ab5ad6180652b3b00d1f5b01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ab5ad6180652b3b00d1f5b01", +"postgres": "ab5ad6180652b3b00d1f5b01", +"postgresql": "ab5ad6180652b3b00d1f5b01", +"snowflake": "226beccc1d430773f4e67b70", +"tsql": "ab5ad6180652b3b00d1f5b01" +} +], +[ +"type", +{ +"DuckDB": "555111267818cbd3d0b07105", +"None": "555111267818cbd3d0b07105", +"bigquery": "555111267818cbd3d0b07105", +"databricks": "555111267818cbd3d0b07105", +"duckdb": "555111267818cbd3d0b07105", +"generic": "555111267818cbd3d0b07105", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "555111267818cbd3d0b07105", +"postgres": "555111267818cbd3d0b07105", +"postgresql": "555111267818cbd3d0b07105", +"snowflake": "555111267818cbd3d0b07105", +"tsql": "555111267818cbd3d0b07105" +} +], +[ +"type", +{ +"DuckDB": "0ca1be8f7d61ca627e620450", +"None": "0ca1be8f7d61ca627e620450", +"bigquery": "e1c36a5cdd2427ff2b089ac9", +"databricks": "0ca1be8f7d61ca627e620450", +"duckdb": "0ca1be8f7d61ca627e620450", +"generic": "0ca1be8f7d61ca627e620450", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0ca1be8f7d61ca627e620450", +"postgres": "0ca1be8f7d61ca627e620450", +"postgresql": "0ca1be8f7d61ca627e620450", +"snowflake": "18db5afe2c1e80d1ee652b54", +"tsql": "0ca1be8f7d61ca627e620450" +} +], +[ +"type", +{ +"DuckDB": "9aff7fcc7d550e1a8d105471", +"None": "9aff7fcc7d550e1a8d105471", +"bigquery": "f9bb9d0c419201aea072940b", +"databricks": "9aff7fcc7d550e1a8d105471", +"duckdb": "9aff7fcc7d550e1a8d105471", +"generic": "9aff7fcc7d550e1a8d105471", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9aff7fcc7d550e1a8d105471", +"postgres": "9aff7fcc7d550e1a8d105471", +"postgresql": "9aff7fcc7d550e1a8d105471", +"snowflake": "9aff7fcc7d550e1a8d105471", +"tsql": "9aff7fcc7d550e1a8d105471" +} +], +[ +"type", +{ +"DuckDB": "e8b6e0bd2ded7b9eec6209e4", +"None": "e8b6e0bd2ded7b9eec6209e4", +"bigquery": "e8b6e0bd2ded7b9eec6209e4", +"databricks": "e8b6e0bd2ded7b9eec6209e4", +"duckdb": "e8b6e0bd2ded7b9eec6209e4", +"generic": "e8b6e0bd2ded7b9eec6209e4", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e8b6e0bd2ded7b9eec6209e4", +"postgres": "e8b6e0bd2ded7b9eec6209e4", +"postgresql": "e8b6e0bd2ded7b9eec6209e4", +"snowflake": "e8b6e0bd2ded7b9eec6209e4", +"tsql": "e8b6e0bd2ded7b9eec6209e4" +} +], +[ +"type", +{ +"DuckDB": "e08a957777bd278f921ca0ee", +"None": "e08a957777bd278f921ca0ee", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "e08a957777bd278f921ca0ee", +"duckdb": "e08a957777bd278f921ca0ee", +"generic": "e08a957777bd278f921ca0ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e08a957777bd278f921ca0ee", +"postgres": "e08a957777bd278f921ca0ee", +"postgresql": "e08a957777bd278f921ca0ee", +"snowflake": "e08a957777bd278f921ca0ee", +"tsql": "e08a957777bd278f921ca0ee" +} +], +[ +"type", +{ +"DuckDB": "da72f481881cd577b304e9e7", +"None": "da72f481881cd577b304e9e7", +"bigquery": "da72f481881cd577b304e9e7", +"databricks": "da72f481881cd577b304e9e7", +"duckdb": "da72f481881cd577b304e9e7", +"generic": "da72f481881cd577b304e9e7", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "da72f481881cd577b304e9e7", +"postgres": "da72f481881cd577b304e9e7", +"postgresql": "da72f481881cd577b304e9e7", +"snowflake": "da72f481881cd577b304e9e7", +"tsql": "da72f481881cd577b304e9e7" +} +], +[ +"type", +{ +"DuckDB": "b0791eeb8731828818f87f78", +"None": "b0791eeb8731828818f87f78", +"bigquery": "b0791eeb8731828818f87f78", +"databricks": "b0791eeb8731828818f87f78", +"duckdb": "b0791eeb8731828818f87f78", +"generic": "b0791eeb8731828818f87f78", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b0791eeb8731828818f87f78", +"postgres": "b0791eeb8731828818f87f78", +"postgresql": "b0791eeb8731828818f87f78", +"snowflake": "b0791eeb8731828818f87f78", +"tsql": "b0791eeb8731828818f87f78" +} +], +[ +"type", +{ +"DuckDB": "d4b3585f1e47f5f60a2c28ee", +"None": "d4b3585f1e47f5f60a2c28ee", +"bigquery": "d4b3585f1e47f5f60a2c28ee", +"databricks": "d4b3585f1e47f5f60a2c28ee", +"duckdb": "d4b3585f1e47f5f60a2c28ee", +"generic": "d4b3585f1e47f5f60a2c28ee", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d4b3585f1e47f5f60a2c28ee", +"postgres": "d4b3585f1e47f5f60a2c28ee", +"postgresql": "d4b3585f1e47f5f60a2c28ee", +"snowflake": "d4b3585f1e47f5f60a2c28ee", +"tsql": "d4b3585f1e47f5f60a2c28ee" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "4b356542044f1267fcfc487a", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "4b356542044f1267fcfc487a", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "5abf4ec799a2ec32fb10da7e", +"bigquery": "5abf4ec799a2ec32fb10da7e", +"databricks": "5abf4ec799a2ec32fb10da7e", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "5abf4ec799a2ec32fb10da7e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5abf4ec799a2ec32fb10da7e", +"postgres": "5abf4ec799a2ec32fb10da7e", +"postgresql": "5abf4ec799a2ec32fb10da7e", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "5abf4ec799a2ec32fb10da7e" +} +], +[ +"type", +{ +"DuckDB": "7ca149227eff68da5bfa0404", +"None": "7ca149227eff68da5bfa0404", +"bigquery": "7ca149227eff68da5bfa0404", +"databricks": "7ca149227eff68da5bfa0404", +"duckdb": "7ca149227eff68da5bfa0404", +"generic": "7ca149227eff68da5bfa0404", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7ca149227eff68da5bfa0404", +"postgres": "7ca149227eff68da5bfa0404", +"postgresql": "7ca149227eff68da5bfa0404", +"snowflake": "7ca149227eff68da5bfa0404", +"tsql": "7ca149227eff68da5bfa0404" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "19cc45192479751e55bffbfe", +"None": "19cc45192479751e55bffbfe", +"bigquery": "19cc45192479751e55bffbfe", +"databricks": "19cc45192479751e55bffbfe", +"duckdb": "19cc45192479751e55bffbfe", +"generic": "19cc45192479751e55bffbfe", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19cc45192479751e55bffbfe", +"postgres": "19cc45192479751e55bffbfe", +"postgresql": "19cc45192479751e55bffbfe", +"snowflake": "19cc45192479751e55bffbfe", +"tsql": "19cc45192479751e55bffbfe" +} +], +[ +"type", +{ +"DuckDB": "300e2b67af892b463d85f9cd", +"None": "300e2b67af892b463d85f9cd", +"bigquery": "300e2b67af892b463d85f9cd", +"databricks": "300e2b67af892b463d85f9cd", +"duckdb": "300e2b67af892b463d85f9cd", +"generic": "300e2b67af892b463d85f9cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "300e2b67af892b463d85f9cd", +"postgres": "300e2b67af892b463d85f9cd", +"postgresql": "300e2b67af892b463d85f9cd", +"snowflake": "300e2b67af892b463d85f9cd", +"tsql": "300e2b67af892b463d85f9cd" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "1e75e5227bea0ae294ac7af1", +"bigquery": "1e75e5227bea0ae294ac7af1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "1e75e5227bea0ae294ac7af1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19bdd92ea0cc5fb028657510", +"postgres": "19bdd92ea0cc5fb028657510", +"postgresql": "19bdd92ea0cc5fb028657510", +"snowflake": "8ff614b7306c0edbe36ce00b", +"tsql": "901e8c830028411846541f65" +} +], +[ +"type", +{ +"DuckDB": "94f6df33eccb97170eccb01b", +"None": "94f6df33eccb97170eccb01b", +"bigquery": "94f6df33eccb97170eccb01b", +"databricks": "94f6df33eccb97170eccb01b", +"duckdb": "94f6df33eccb97170eccb01b", +"generic": "94f6df33eccb97170eccb01b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "94f6df33eccb97170eccb01b", +"postgres": "94f6df33eccb97170eccb01b", +"postgresql": "94f6df33eccb97170eccb01b", +"snowflake": "94f6df33eccb97170eccb01b", +"tsql": "94f6df33eccb97170eccb01b" +} +], +[ +"type", +{ +"DuckDB": "19a1a80ffc06169f32694de8", +"None": "19a1a80ffc06169f32694de8", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "19a1a80ffc06169f32694de8", +"duckdb": "19a1a80ffc06169f32694de8", +"generic": "19a1a80ffc06169f32694de8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "19a1a80ffc06169f32694de8", +"postgres": "19a1a80ffc06169f32694de8", +"postgresql": "19a1a80ffc06169f32694de8", +"snowflake": "19a1a80ffc06169f32694de8", +"tsql": "19a1a80ffc06169f32694de8" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "de89632e277dbc8c97115419", +"None": "de89632e277dbc8c97115419", +"bigquery": "de89632e277dbc8c97115419", +"databricks": "de89632e277dbc8c97115419", +"duckdb": "de89632e277dbc8c97115419", +"generic": "de89632e277dbc8c97115419", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "de89632e277dbc8c97115419", +"postgres": "de89632e277dbc8c97115419", +"postgresql": "de89632e277dbc8c97115419", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "de89632e277dbc8c97115419" +} +], +[ +"type", +{ +"DuckDB": "2444c03418f9f1b2f23f2ec8", +"None": "2444c03418f9f1b2f23f2ec8", +"bigquery": "2444c03418f9f1b2f23f2ec8", +"databricks": "2444c03418f9f1b2f23f2ec8", +"duckdb": "2444c03418f9f1b2f23f2ec8", +"generic": "2444c03418f9f1b2f23f2ec8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2444c03418f9f1b2f23f2ec8", +"postgres": "2444c03418f9f1b2f23f2ec8", +"postgresql": "2444c03418f9f1b2f23f2ec8", +"snowflake": "2444c03418f9f1b2f23f2ec8", +"tsql": "2444c03418f9f1b2f23f2ec8" +} +], +[ +"type", +{ +"DuckDB": "f7fca17b74cc50ae3de8c826", +"None": "d2c97d221599d038d9875873", +"bigquery": "d2c97d221599d038d9875873", +"databricks": "d2c97d221599d038d9875873", +"duckdb": "f7fca17b74cc50ae3de8c826", +"generic": "d2c97d221599d038d9875873", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d2c97d221599d038d9875873", +"postgres": "d2c97d221599d038d9875873", +"postgresql": "d2c97d221599d038d9875873", +"snowflake": "d2c97d221599d038d9875873", +"tsql": "d2c97d221599d038d9875873" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "787213ce6b6e210ded1d752c", +"None": "787213ce6b6e210ded1d752c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "787213ce6b6e210ded1d752c", +"duckdb": "787213ce6b6e210ded1d752c", +"generic": "787213ce6b6e210ded1d752c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "787213ce6b6e210ded1d752c", +"postgres": "787213ce6b6e210ded1d752c", +"postgresql": "787213ce6b6e210ded1d752c", +"snowflake": "b561884132dbe7e42b719110", +"tsql": "787213ce6b6e210ded1d752c" +} +], +[ +"type", +{ +"DuckDB": "63c80b5d8e861216f0c45bfd", +"None": "f8219c960720043f0662a603", +"bigquery": "f8219c960720043f0662a603", +"databricks": "f8219c960720043f0662a603", +"duckdb": "63c80b5d8e861216f0c45bfd", +"generic": "f8219c960720043f0662a603", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f8219c960720043f0662a603", +"postgres": "f8219c960720043f0662a603", +"postgresql": "f8219c960720043f0662a603", +"snowflake": "f8219c960720043f0662a603", +"tsql": "f8219c960720043f0662a603" +} +], +[ +"type", +{ +"DuckDB": "396b2ce32efb5365b393c230", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "396b2ce32efb5365b393c230", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "f16105cbc61d547e6c08efc8", +"None": "f16105cbc61d547e6c08efc8", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "f16105cbc61d547e6c08efc8", +"duckdb": "f16105cbc61d547e6c08efc8", +"generic": "f16105cbc61d547e6c08efc8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f16105cbc61d547e6c08efc8", +"postgres": "f16105cbc61d547e6c08efc8", +"postgresql": "f16105cbc61d547e6c08efc8", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "f16105cbc61d547e6c08efc8" +} +], +[ +"type", +{ +"DuckDB": "f6140be64a327a15b42b923d", +"None": "f6140be64a327a15b42b923d", +"bigquery": "f6140be64a327a15b42b923d", +"databricks": "f6140be64a327a15b42b923d", +"duckdb": "f6140be64a327a15b42b923d", +"generic": "f6140be64a327a15b42b923d", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f6140be64a327a15b42b923d", +"postgres": "f6140be64a327a15b42b923d", +"postgresql": "f6140be64a327a15b42b923d", +"snowflake": "f6140be64a327a15b42b923d", +"tsql": "f6140be64a327a15b42b923d" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "25cdfa949946952336077d37", +"None": "25cdfa949946952336077d37", +"bigquery": "25cdfa949946952336077d37", +"databricks": "25cdfa949946952336077d37", +"duckdb": "25cdfa949946952336077d37", +"generic": "25cdfa949946952336077d37", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "25cdfa949946952336077d37", +"postgres": "25cdfa949946952336077d37", +"postgresql": "25cdfa949946952336077d37", +"snowflake": "25cdfa949946952336077d37", +"tsql": "25cdfa949946952336077d37" +} +], +[ +"type", +{ +"DuckDB": "6d7ab232e65fe8d85c7169ae", +"None": "6d7ab232e65fe8d85c7169ae", +"bigquery": "6f6198e2d2c8a8d6444ef5af", +"databricks": "6d7ab232e65fe8d85c7169ae", +"duckdb": "6d7ab232e65fe8d85c7169ae", +"generic": "6d7ab232e65fe8d85c7169ae", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6d7ab232e65fe8d85c7169ae", +"postgres": "a6659d96bbfa5da75e69e22d", +"postgresql": "a6659d96bbfa5da75e69e22d", +"snowflake": "6d7ab232e65fe8d85c7169ae", +"tsql": "aed0419e7f2ce7c0a8892248" +} +], +[ +"type", +{ +"DuckDB": "13bc61bc9a039c8b798c5322", +"None": "1509a96170cb94e0a99bbf00", +"bigquery": "9193e8385b52630e5bac3baf", +"databricks": "1509a96170cb94e0a99bbf00", +"duckdb": "13bc61bc9a039c8b798c5322", +"generic": "1509a96170cb94e0a99bbf00", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "13bc61bc9a039c8b798c5322", +"postgresql": "13bc61bc9a039c8b798c5322", +"snowflake": "d5cc04b4579263e52cd194bb", +"tsql": "29b39772c27728dfa6b9e0b2" +} +], +[ +"type", +{ +"DuckDB": "d6de1c22b8b95cc863db57ba", +"None": "d6de1c22b8b95cc863db57ba", +"bigquery": "d6de1c22b8b95cc863db57ba", +"databricks": "d6de1c22b8b95cc863db57ba", +"duckdb": "d6de1c22b8b95cc863db57ba", +"generic": "d6de1c22b8b95cc863db57ba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d6de1c22b8b95cc863db57ba", +"postgres": "d6de1c22b8b95cc863db57ba", +"postgresql": "d6de1c22b8b95cc863db57ba", +"snowflake": "d6de1c22b8b95cc863db57ba", +"tsql": "d6de1c22b8b95cc863db57ba" +} +], +[ +"type", +{ +"DuckDB": "2c6b371b030464f525066893", +"None": "2c6b371b030464f525066893", +"bigquery": "2c6b371b030464f525066893", +"databricks": "2c6b371b030464f525066893", +"duckdb": "2c6b371b030464f525066893", +"generic": "2c6b371b030464f525066893", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2c6b371b030464f525066893", +"postgres": "2c6b371b030464f525066893", +"postgresql": "2c6b371b030464f525066893", +"snowflake": "2c6b371b030464f525066893", +"tsql": "2c6b371b030464f525066893" +} +], +[ +"type", +{ +"DuckDB": "35e216b6921bf843994f2eea", +"None": "46ec3e19e05a6cb29854ccac", +"bigquery": "46ec3e19e05a6cb29854ccac", +"databricks": "46ec3e19e05a6cb29854ccac", +"duckdb": "35e216b6921bf843994f2eea", +"generic": "46ec3e19e05a6cb29854ccac", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "46ec3e19e05a6cb29854ccac", +"postgres": "46ec3e19e05a6cb29854ccac", +"postgresql": "46ec3e19e05a6cb29854ccac", +"snowflake": "46ec3e19e05a6cb29854ccac", +"tsql": "46ec3e19e05a6cb29854ccac" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "2fcd11f2fdbe6c4498c4833e", +"None": "2fcd11f2fdbe6c4498c4833e", +"bigquery": "2fcd11f2fdbe6c4498c4833e", +"databricks": "2fcd11f2fdbe6c4498c4833e", +"duckdb": "2fcd11f2fdbe6c4498c4833e", +"generic": "2fcd11f2fdbe6c4498c4833e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2fcd11f2fdbe6c4498c4833e", +"postgres": "2fcd11f2fdbe6c4498c4833e", +"postgresql": "2fcd11f2fdbe6c4498c4833e", +"snowflake": "2fcd11f2fdbe6c4498c4833e", +"tsql": "2fcd11f2fdbe6c4498c4833e" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "c844c96e1e3ffc72d5f90df9", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "c844c96e1e3ffc72d5f90df9", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "c844c96e1e3ffc72d5f90df9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "c844c96e1e3ffc72d5f90df9", +"postgresql": "c844c96e1e3ffc72d5f90df9", +"snowflake": "c844c96e1e3ffc72d5f90df9", +"tsql": "a4a176ec3c56e2704a650963" +} +], +[ +"type", +{ +"DuckDB": "5c28019dce48a74708c90a76", +"None": "5c28019dce48a74708c90a76", +"bigquery": "f28a96f0eb38ad8874f6ec5d", +"databricks": "5c28019dce48a74708c90a76", +"duckdb": "5c28019dce48a74708c90a76", +"generic": "5c28019dce48a74708c90a76", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "5c28019dce48a74708c90a76", +"postgres": "5c28019dce48a74708c90a76", +"postgresql": "5c28019dce48a74708c90a76", +"snowflake": "a5656d70e208254d1d3e9b4f", +"tsql": "2a7a7132adc795ca2bc39c37" +} +], +[ +"type", +{ +"DuckDB": "2fcd11f2fdbe6c4498c4833e", +"None": "2fcd11f2fdbe6c4498c4833e", +"bigquery": "2fcd11f2fdbe6c4498c4833e", +"databricks": "2fcd11f2fdbe6c4498c4833e", +"duckdb": "2fcd11f2fdbe6c4498c4833e", +"generic": "2fcd11f2fdbe6c4498c4833e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2fcd11f2fdbe6c4498c4833e", +"postgres": "2fcd11f2fdbe6c4498c4833e", +"postgresql": "2fcd11f2fdbe6c4498c4833e", +"snowflake": "2fcd11f2fdbe6c4498c4833e", +"tsql": "2fcd11f2fdbe6c4498c4833e" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "6f4372a332076048e56d5076", +"None": "6f4372a332076048e56d5076", +"bigquery": "74a30f6aa9def0b58afc616c", +"databricks": "6f4372a332076048e56d5076", +"duckdb": "6f4372a332076048e56d5076", +"generic": "6f4372a332076048e56d5076", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6f4372a332076048e56d5076", +"postgres": "6f4372a332076048e56d5076", +"postgresql": "6f4372a332076048e56d5076", +"snowflake": "daeeb0f00b3ee4211b8da00f", +"tsql": "6f4372a332076048e56d5076" +} +], +[ +"type", +{ +"DuckDB": "7a00586f9fdb1d97256bfff8", +"None": "7a00586f9fdb1d97256bfff8", +"bigquery": "7a00586f9fdb1d97256bfff8", +"databricks": "7a00586f9fdb1d97256bfff8", +"duckdb": "7a00586f9fdb1d97256bfff8", +"generic": "7a00586f9fdb1d97256bfff8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7a00586f9fdb1d97256bfff8", +"postgres": "7a00586f9fdb1d97256bfff8", +"postgresql": "7a00586f9fdb1d97256bfff8", +"snowflake": "7a00586f9fdb1d97256bfff8", +"tsql": "7a00586f9fdb1d97256bfff8" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "aab0389e2b5470136660bf8a", +"None": "aab0389e2b5470136660bf8a", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "aab0389e2b5470136660bf8a", +"duckdb": "aab0389e2b5470136660bf8a", +"generic": "aab0389e2b5470136660bf8a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "aab0389e2b5470136660bf8a", +"postgres": "aab0389e2b5470136660bf8a", +"postgresql": "aab0389e2b5470136660bf8a", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "aab0389e2b5470136660bf8a" +} +], +[ +"type", +{ +"DuckDB": "62368fc15cefd5807807b209", +"None": "62368fc15cefd5807807b209", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "62368fc15cefd5807807b209", +"duckdb": "62368fc15cefd5807807b209", +"generic": "62368fc15cefd5807807b209", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "62368fc15cefd5807807b209", +"postgres": "62368fc15cefd5807807b209", +"postgresql": "62368fc15cefd5807807b209", +"snowflake": "62368fc15cefd5807807b209", +"tsql": "62368fc15cefd5807807b209" +} +], +[ +"type", +{ +"DuckDB": "38b2ec94bf02be40007ce525", +"None": "38b2ec94bf02be40007ce525", +"bigquery": "38b2ec94bf02be40007ce525", +"databricks": "38b2ec94bf02be40007ce525", +"duckdb": "38b2ec94bf02be40007ce525", +"generic": "38b2ec94bf02be40007ce525", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "38b2ec94bf02be40007ce525", +"postgres": "38b2ec94bf02be40007ce525", +"postgresql": "38b2ec94bf02be40007ce525", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "38b2ec94bf02be40007ce525" +} +], +[ +"type", +{ +"DuckDB": "89cc3662f602a13aef39eb80", +"None": "f2bbf59a9c02f59ca89e66b6", +"bigquery": "f2bbf59a9c02f59ca89e66b6", +"databricks": "f2bbf59a9c02f59ca89e66b6", +"duckdb": "89cc3662f602a13aef39eb80", +"generic": "f2bbf59a9c02f59ca89e66b6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "89cc3662f602a13aef39eb80", +"postgresql": "89cc3662f602a13aef39eb80", +"snowflake": "6f88ef12d95c26bf8f0bd9e5", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "0703bf71a54942bdaa28406e", +"None": "0703bf71a54942bdaa28406e", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "0703bf71a54942bdaa28406e", +"duckdb": "0703bf71a54942bdaa28406e", +"generic": "0703bf71a54942bdaa28406e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0703bf71a54942bdaa28406e", +"postgres": "0703bf71a54942bdaa28406e", +"postgresql": "0703bf71a54942bdaa28406e", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "441f377c46ddc162ec0a5976" +} +], +[ +"type", +{ +"DuckDB": "ef2689646fd9e772e27d2727", +"None": "ef2689646fd9e772e27d2727", +"bigquery": "ef2689646fd9e772e27d2727", +"databricks": "e63b3aab09c82955e2560928", +"duckdb": "ef2689646fd9e772e27d2727", +"generic": "ef2689646fd9e772e27d2727", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "ef2689646fd9e772e27d2727", +"postgresql": "ef2689646fd9e772e27d2727", +"snowflake": "ef2689646fd9e772e27d2727", +"tsql": "ef2689646fd9e772e27d2727" +} +], +[ +"type", +{ +"DuckDB": "c43574432afc90bcba6944b2", +"None": "7040afbb3a088baf55e328b2", +"bigquery": "7040afbb3a088baf55e328b2", +"databricks": "ba611057434e43344935471b", +"duckdb": "c43574432afc90bcba6944b2", +"generic": "7040afbb3a088baf55e328b2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "1b3243409566f14d81c11fff", +"postgresql": "1b3243409566f14d81c11fff", +"snowflake": "5b6535982c7d4576e34ee7cb", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "b46dfa976d5ba04f95bea32b", +"None": "b46dfa976d5ba04f95bea32b", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "b46dfa976d5ba04f95bea32b", +"duckdb": "b46dfa976d5ba04f95bea32b", +"generic": "b46dfa976d5ba04f95bea32b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b46dfa976d5ba04f95bea32b", +"postgres": "34dcdfe3e3afe6140e1353e0", +"postgresql": "34dcdfe3e3afe6140e1353e0", +"snowflake": "b46dfa976d5ba04f95bea32b", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "bc9875824f2c0eb90fcb5e51", +"None": "0289c868616e51dc348bba36", +"bigquery": "9255d53d9bcd9cb04141e55f", +"databricks": "031c4b14dc8b97fdb38e28b3", +"duckdb": "bc9875824f2c0eb90fcb5e51", +"generic": "0289c868616e51dc348bba36", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe2c1943dc029130eda3f066", +"postgres": "fe2c1943dc029130eda3f066", +"postgresql": "fe2c1943dc029130eda3f066", +"snowflake": "ffc35af553194c8ccffc9058", +"tsql": "fe2c1943dc029130eda3f066" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "c844c96e1e3ffc72d5f90df9", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "c844c96e1e3ffc72d5f90df9", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "c844c96e1e3ffc72d5f90df9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "c844c96e1e3ffc72d5f90df9", +"postgresql": "c844c96e1e3ffc72d5f90df9", +"snowflake": "c844c96e1e3ffc72d5f90df9", +"tsql": "a4a176ec3c56e2704a650963" +} +], +[ +"type", +{ +"DuckDB": "3dbbe73ee4310c73030e8110", +"None": "3dbbe73ee4310c73030e8110", +"bigquery": "3dbbe73ee4310c73030e8110", +"databricks": "3dbbe73ee4310c73030e8110", +"duckdb": "3dbbe73ee4310c73030e8110", +"generic": "3dbbe73ee4310c73030e8110", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3dbbe73ee4310c73030e8110", +"postgres": "3dbbe73ee4310c73030e8110", +"postgresql": "3dbbe73ee4310c73030e8110", +"snowflake": "f43dac38f34a1c0618a3f812", +"tsql": "3dbbe73ee4310c73030e8110" +} +], +[ +"type", +{ +"DuckDB": "8f29d402cac8d3b922d1a4e7", +"None": "04d8552335eed867485d59b8", +"bigquery": "04d8552335eed867485d59b8", +"databricks": "04d8552335eed867485d59b8", +"duckdb": "8f29d402cac8d3b922d1a4e7", +"generic": "04d8552335eed867485d59b8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "8f29d402cac8d3b922d1a4e7", +"postgresql": "8f29d402cac8d3b922d1a4e7", +"snowflake": "c60d34b521887e82716fd787", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "d53793d8f8d21c36a117dedb", +"None": "d53793d8f8d21c36a117dedb", +"bigquery": "d53793d8f8d21c36a117dedb", +"databricks": "d53793d8f8d21c36a117dedb", +"duckdb": "d53793d8f8d21c36a117dedb", +"generic": "d53793d8f8d21c36a117dedb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d53793d8f8d21c36a117dedb", +"postgres": "d53793d8f8d21c36a117dedb", +"postgresql": "d53793d8f8d21c36a117dedb", +"snowflake": "d53793d8f8d21c36a117dedb", +"tsql": "d53793d8f8d21c36a117dedb" +} +], +[ +"type", +{ +"DuckDB": "251aba3fef503ce64556c701", +"None": "251aba3fef503ce64556c701", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "251aba3fef503ce64556c701", +"duckdb": "251aba3fef503ce64556c701", +"generic": "251aba3fef503ce64556c701", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "251aba3fef503ce64556c701", +"postgres": "b7d6fc051a3c1ea6deb29ff4", +"postgresql": "b7d6fc051a3c1ea6deb29ff4", +"snowflake": "251aba3fef503ce64556c701", +"tsql": "e3bc1b8bad22d8104417b00f" +} +], +[ +"type", +{ +"DuckDB": "df19c722959a4259621a1e09", +"None": "df19c722959a4259621a1e09", +"bigquery": "df19c722959a4259621a1e09", +"databricks": "df19c722959a4259621a1e09", +"duckdb": "df19c722959a4259621a1e09", +"generic": "df19c722959a4259621a1e09", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "df19c722959a4259621a1e09", +"postgres": "df19c722959a4259621a1e09", +"postgresql": "df19c722959a4259621a1e09", +"snowflake": "df19c722959a4259621a1e09", +"tsql": "df19c722959a4259621a1e09" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "4662a1758442ce44b830269f", +"None": "4662a1758442ce44b830269f", +"bigquery": "4662a1758442ce44b830269f", +"databricks": "4662a1758442ce44b830269f", +"duckdb": "4662a1758442ce44b830269f", +"generic": "4662a1758442ce44b830269f", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4662a1758442ce44b830269f", +"postgres": "4662a1758442ce44b830269f", +"postgresql": "4662a1758442ce44b830269f", +"snowflake": "4662a1758442ce44b830269f", +"tsql": "4662a1758442ce44b830269f" +} +], +[ +"type", +{ +"DuckDB": "73975a560983dfcdc361f145", +"None": "73975a560983dfcdc361f145", +"bigquery": "73975a560983dfcdc361f145", +"databricks": "73975a560983dfcdc361f145", +"duckdb": "73975a560983dfcdc361f145", +"generic": "73975a560983dfcdc361f145", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "73975a560983dfcdc361f145", +"postgres": "73975a560983dfcdc361f145", +"postgresql": "73975a560983dfcdc361f145", +"snowflake": "73975a560983dfcdc361f145", +"tsql": "73975a560983dfcdc361f145" +} +], +[ +"type", +{ +"DuckDB": "8a3f094f18fb4047f0febf59", +"None": "c844c96e1e3ffc72d5f90df9", +"bigquery": "5c28019dce48a74708c90a76", +"databricks": "c844c96e1e3ffc72d5f90df9", +"duckdb": "8a3f094f18fb4047f0febf59", +"generic": "c844c96e1e3ffc72d5f90df9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "28eb0d6de9724959ce5ba43e", +"postgres": "c844c96e1e3ffc72d5f90df9", +"postgresql": "c844c96e1e3ffc72d5f90df9", +"snowflake": "c844c96e1e3ffc72d5f90df9", +"tsql": "a4a176ec3c56e2704a650963" +} +], +[ +"type", +{ +"DuckDB": "fedd65248cdf63b24b8c1567", +"None": "fedd65248cdf63b24b8c1567", +"bigquery": "fedd65248cdf63b24b8c1567", +"databricks": "fedd65248cdf63b24b8c1567", +"duckdb": "fedd65248cdf63b24b8c1567", +"generic": "fedd65248cdf63b24b8c1567", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fedd65248cdf63b24b8c1567", +"postgres": "fedd65248cdf63b24b8c1567", +"postgresql": "fedd65248cdf63b24b8c1567", +"snowflake": "fedd65248cdf63b24b8c1567", +"tsql": "fedd65248cdf63b24b8c1567" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "d79c3ac70093cb4cb531e71c", +"None": "d79c3ac70093cb4cb531e71c", +"bigquery": "b0477b36767a62145aa9158c", +"databricks": "d79c3ac70093cb4cb531e71c", +"duckdb": "d79c3ac70093cb4cb531e71c", +"generic": "d79c3ac70093cb4cb531e71c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d79c3ac70093cb4cb531e71c", +"postgres": "d79c3ac70093cb4cb531e71c", +"postgresql": "d79c3ac70093cb4cb531e71c", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d79c3ac70093cb4cb531e71c" +} +], +[ +"type", +{ +"DuckDB": "3673b4fbbd8a7fed7120ec2b", +"None": "3673b4fbbd8a7fed7120ec2b", +"bigquery": "3673b4fbbd8a7fed7120ec2b", +"databricks": "3673b4fbbd8a7fed7120ec2b", +"duckdb": "3673b4fbbd8a7fed7120ec2b", +"generic": "3673b4fbbd8a7fed7120ec2b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3673b4fbbd8a7fed7120ec2b", +"postgres": "3673b4fbbd8a7fed7120ec2b", +"postgresql": "3673b4fbbd8a7fed7120ec2b", +"snowflake": "3673b4fbbd8a7fed7120ec2b", +"tsql": "3673b4fbbd8a7fed7120ec2b" +} +], +[ +"type", +{ +"DuckDB": "d2e8f9f7288b371199b1652e", +"None": "9bca83e64bfacf0a80ca9b34", +"bigquery": "fe1b4290ba0c2395d949643d", +"databricks": "fe1b4290ba0c2395d949643d", +"duckdb": "d2e8f9f7288b371199b1652e", +"generic": "9bca83e64bfacf0a80ca9b34", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe1b4290ba0c2395d949643d", +"postgres": "d2e8f9f7288b371199b1652e", +"postgresql": "d2e8f9f7288b371199b1652e", +"snowflake": "c326a07d4a8adffde91e55cd", +"tsql": "fe1b4290ba0c2395d949643d" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "77ffdb80029bd6e2cca768ad", +"None": "77ffdb80029bd6e2cca768ad", +"bigquery": "77ffdb80029bd6e2cca768ad", +"databricks": "77ffdb80029bd6e2cca768ad", +"duckdb": "77ffdb80029bd6e2cca768ad", +"generic": "77ffdb80029bd6e2cca768ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "77ffdb80029bd6e2cca768ad", +"postgres": "77ffdb80029bd6e2cca768ad", +"postgresql": "77ffdb80029bd6e2cca768ad", +"snowflake": "77ffdb80029bd6e2cca768ad", +"tsql": "77ffdb80029bd6e2cca768ad" +} +], +[ +"type", +{ +"DuckDB": "b69a57be16780e0a5e4acadb", +"None": "b69a57be16780e0a5e4acadb", +"bigquery": "b69a57be16780e0a5e4acadb", +"databricks": "b69a57be16780e0a5e4acadb", +"duckdb": "b69a57be16780e0a5e4acadb", +"generic": "b69a57be16780e0a5e4acadb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b69a57be16780e0a5e4acadb", +"postgres": "b69a57be16780e0a5e4acadb", +"postgresql": "b69a57be16780e0a5e4acadb", +"snowflake": "b69a57be16780e0a5e4acadb", +"tsql": "b69a57be16780e0a5e4acadb" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "f1625f38cf20f9627bc717a9", +"None": "f1625f38cf20f9627bc717a9", +"bigquery": "f1625f38cf20f9627bc717a9", +"databricks": "f1625f38cf20f9627bc717a9", +"duckdb": "f1625f38cf20f9627bc717a9", +"generic": "f1625f38cf20f9627bc717a9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "f1625f38cf20f9627bc717a9", +"postgres": "f1625f38cf20f9627bc717a9", +"postgresql": "f1625f38cf20f9627bc717a9", +"snowflake": "f1625f38cf20f9627bc717a9", +"tsql": "f1625f38cf20f9627bc717a9" +} +], +[ +"type", +{ +"DuckDB": "e998cd8f0f74af0526b75ff0", +"None": "e998cd8f0f74af0526b75ff0", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "e998cd8f0f74af0526b75ff0", +"duckdb": "e998cd8f0f74af0526b75ff0", +"generic": "e998cd8f0f74af0526b75ff0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e998cd8f0f74af0526b75ff0", +"postgres": "e998cd8f0f74af0526b75ff0", +"postgresql": "e998cd8f0f74af0526b75ff0", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "e998cd8f0f74af0526b75ff0" +} +], +[ +"type", +{ +"DuckDB": "fa45b7e4a9ef3b5a9c5781af", +"None": "fa45b7e4a9ef3b5a9c5781af", +"bigquery": "fa45b7e4a9ef3b5a9c5781af", +"databricks": "fa45b7e4a9ef3b5a9c5781af", +"duckdb": "fa45b7e4a9ef3b5a9c5781af", +"generic": "fa45b7e4a9ef3b5a9c5781af", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fa45b7e4a9ef3b5a9c5781af", +"postgres": "fa45b7e4a9ef3b5a9c5781af", +"postgresql": "fa45b7e4a9ef3b5a9c5781af", +"snowflake": "fa45b7e4a9ef3b5a9c5781af", +"tsql": "fa45b7e4a9ef3b5a9c5781af" +} +], +[ +"type", +{ +"DuckDB": "5abf4ec799a2ec32fb10da7e", +"None": "4b356542044f1267fcfc487a", +"bigquery": "4b356542044f1267fcfc487a", +"databricks": "4b356542044f1267fcfc487a", +"duckdb": "5abf4ec799a2ec32fb10da7e", +"generic": "4b356542044f1267fcfc487a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "4b356542044f1267fcfc487a", +"postgres": "4b356542044f1267fcfc487a", +"postgresql": "4b356542044f1267fcfc487a", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "4b356542044f1267fcfc487a" +} +], +[ +"type", +{ +"DuckDB": "1ebac487198b6c90b027faab", +"None": "1ebac487198b6c90b027faab", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "1ebac487198b6c90b027faab", +"duckdb": "1ebac487198b6c90b027faab", +"generic": "1ebac487198b6c90b027faab", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1ebac487198b6c90b027faab", +"postgres": "1ebac487198b6c90b027faab", +"postgresql": "1ebac487198b6c90b027faab", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "c692d974ee3f362ecb38f27c" +} +], +[ +"type", +{ +"DuckDB": "1fe8a2d5a41383bd8e502808", +"None": "1fe8a2d5a41383bd8e502808", +"bigquery": "70f3d045595fbd43219efb36", +"databricks": "70f3d045595fbd43219efb36", +"duckdb": "1fe8a2d5a41383bd8e502808", +"generic": "1fe8a2d5a41383bd8e502808", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70f3d045595fbd43219efb36", +"postgres": "1fe8a2d5a41383bd8e502808", +"postgresql": "1fe8a2d5a41383bd8e502808", +"snowflake": "1fe8a2d5a41383bd8e502808", +"tsql": "1fe8a2d5a41383bd8e502808" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "035bb58fd2c7dca585a5899c", +"None": "035bb58fd2c7dca585a5899c", +"bigquery": "035bb58fd2c7dca585a5899c", +"databricks": "958e791772d9007024c889d3", +"duckdb": "035bb58fd2c7dca585a5899c", +"generic": "035bb58fd2c7dca585a5899c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "770b1d275ab7799c8484f304", +"postgres": "035bb58fd2c7dca585a5899c", +"postgresql": "035bb58fd2c7dca585a5899c", +"snowflake": "035bb58fd2c7dca585a5899c", +"tsql": "035bb58fd2c7dca585a5899c" +} +], +[ +"type", +{ +"DuckDB": "7515c0db413239044adef879", +"None": "7515c0db413239044adef879", +"bigquery": "f276e907770196606577c932", +"databricks": "7515c0db413239044adef879", +"duckdb": "7515c0db413239044adef879", +"generic": "7515c0db413239044adef879", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7515c0db413239044adef879", +"postgres": "7515c0db413239044adef879", +"postgresql": "7515c0db413239044adef879", +"snowflake": "67b5874a2c569226c490211f", +"tsql": "7515c0db413239044adef879" +} +], +[ +"type", +{ +"DuckDB": "a3b6b25265be330ec52a3322", +"None": "a3b6b25265be330ec52a3322", +"bigquery": "204206d1d1eecf007966cc5b", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "a3b6b25265be330ec52a3322", +"generic": "a3b6b25265be330ec52a3322", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "919b7ec6a7b49d8527b784cd", +"postgres": "a3b6b25265be330ec52a3322", +"postgresql": "a3b6b25265be330ec52a3322", +"snowflake": "919b7ec6a7b49d8527b784cd", +"tsql": "a3b6b25265be330ec52a3322" +} +], +[ +"type", +{ +"DuckDB": "d61c83ef8bfdf043fa672cb5", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "d61c83ef8bfdf043fa672cb5", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "268b9b7c7862c9de6569286a", +"None": "268b9b7c7862c9de6569286a", +"bigquery": "268b9b7c7862c9de6569286a", +"databricks": "268b9b7c7862c9de6569286a", +"duckdb": "268b9b7c7862c9de6569286a", +"generic": "268b9b7c7862c9de6569286a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "268b9b7c7862c9de6569286a", +"postgres": "268b9b7c7862c9de6569286a", +"postgresql": "268b9b7c7862c9de6569286a", +"snowflake": "268b9b7c7862c9de6569286a", +"tsql": "268b9b7c7862c9de6569286a" +} +], +[ +"type", +{ +"DuckDB": "f6c3d80bfee0ec0b286b82b5", +"None": "b137c998bdaa2555d912e8fa", +"bigquery": "b137c998bdaa2555d912e8fa", +"databricks": "b137c998bdaa2555d912e8fa", +"duckdb": "f6c3d80bfee0ec0b286b82b5", +"generic": "b137c998bdaa2555d912e8fa", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b137c998bdaa2555d912e8fa", +"postgres": "b137c998bdaa2555d912e8fa", +"postgresql": "b137c998bdaa2555d912e8fa", +"snowflake": "b137c998bdaa2555d912e8fa", +"tsql": "b137c998bdaa2555d912e8fa" +} +], +[ +"type", +{ +"DuckDB": "4ed568ea1adb2c8a7141be20", +"None": "b981394a3f4d961a44e73868", +"bigquery": "f65845aac707b13992e53f87", +"databricks": "b981394a3f4d961a44e73868", +"duckdb": "4ed568ea1adb2c8a7141be20", +"generic": "b981394a3f4d961a44e73868", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "4ed568ea1adb2c8a7141be20", +"postgresql": "4ed568ea1adb2c8a7141be20", +"snowflake": "311749caf0bd53fee7b23fa1", +"tsql": "8e94482f81cd02e976359898" +} +], +[ +"type", +{ +"DuckDB": "d5b8a68e96a7848f2aa7b2d6", +"None": "d5b8a68e96a7848f2aa7b2d6", +"bigquery": "316ab5126bbd68359eedc4d9", +"databricks": "d5b8a68e96a7848f2aa7b2d6", +"duckdb": "d5b8a68e96a7848f2aa7b2d6", +"generic": "d5b8a68e96a7848f2aa7b2d6", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d5b8a68e96a7848f2aa7b2d6", +"postgres": "d5b8a68e96a7848f2aa7b2d6", +"postgresql": "d5b8a68e96a7848f2aa7b2d6", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "d5b8a68e96a7848f2aa7b2d6" +} +], +[ +"type", +{ +"DuckDB": "1fa913156c869f5373066b2b", +"None": "1fa913156c869f5373066b2b", +"bigquery": "1fa913156c869f5373066b2b", +"databricks": "1fa913156c869f5373066b2b", +"duckdb": "1fa913156c869f5373066b2b", +"generic": "1fa913156c869f5373066b2b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1fa913156c869f5373066b2b", +"postgres": "1fa913156c869f5373066b2b", +"postgresql": "1fa913156c869f5373066b2b", +"snowflake": "1fa913156c869f5373066b2b", +"tsql": "1fa913156c869f5373066b2b" +} +], +[ +"type", +{ +"DuckDB": "3673b4fbbd8a7fed7120ec2b", +"None": "3673b4fbbd8a7fed7120ec2b", +"bigquery": "3673b4fbbd8a7fed7120ec2b", +"databricks": "3673b4fbbd8a7fed7120ec2b", +"duckdb": "3673b4fbbd8a7fed7120ec2b", +"generic": "3673b4fbbd8a7fed7120ec2b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3673b4fbbd8a7fed7120ec2b", +"postgres": "3673b4fbbd8a7fed7120ec2b", +"postgresql": "3673b4fbbd8a7fed7120ec2b", +"snowflake": "3673b4fbbd8a7fed7120ec2b", +"tsql": "3673b4fbbd8a7fed7120ec2b" +} +], +[ +"type", +{ +"DuckDB": "7770c7aba92f1bba835ef3f8", +"None": "7770c7aba92f1bba835ef3f8", +"bigquery": "7770c7aba92f1bba835ef3f8", +"databricks": "7770c7aba92f1bba835ef3f8", +"duckdb": "7770c7aba92f1bba835ef3f8", +"generic": "7770c7aba92f1bba835ef3f8", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7770c7aba92f1bba835ef3f8", +"postgres": "7770c7aba92f1bba835ef3f8", +"postgresql": "7770c7aba92f1bba835ef3f8", +"snowflake": "7770c7aba92f1bba835ef3f8", +"tsql": "7770c7aba92f1bba835ef3f8" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "e57ac1541fb51b362cd5b470", +"None": "e57ac1541fb51b362cd5b470", +"bigquery": "e57ac1541fb51b362cd5b470", +"databricks": "e57ac1541fb51b362cd5b470", +"duckdb": "e57ac1541fb51b362cd5b470", +"generic": "e57ac1541fb51b362cd5b470", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "e57ac1541fb51b362cd5b470", +"postgres": "e57ac1541fb51b362cd5b470", +"postgresql": "e57ac1541fb51b362cd5b470", +"snowflake": "4b356542044f1267fcfc487a", +"tsql": "e57ac1541fb51b362cd5b470" +} +], +[ +"type", +{ +"DuckDB": "436bf2624179c536a0157841", +"None": "3c6e55e3040bec9d73dd1bae", +"bigquery": "f1e1c64039c39ec114b5ede6", +"databricks": "21bc62fef8fe5716ed99f7be", +"duckdb": "436bf2624179c536a0157841", +"generic": "3c6e55e3040bec9d73dd1bae", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "3c6e55e3040bec9d73dd1bae", +"postgres": "8141c88dbee0d916c5447a97", +"postgresql": "8141c88dbee0d916c5447a97", +"snowflake": "cfa49b8de6f3939e2701f38d", +"tsql": "28f1e600e17067938d8a74f4" +} +], +[ +"type", +{ +"DuckDB": "431ce1fed4f6ce1e6c2a6479", +"None": "6282d36888b8e0aadda09779", +"bigquery": "9da8d0485dcb32b3b63f0ed3", +"databricks": "6282d36888b8e0aadda09779", +"duckdb": "431ce1fed4f6ce1e6c2a6479", +"generic": "6282d36888b8e0aadda09779", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "431ce1fed4f6ce1e6c2a6479", +"postgresql": "431ce1fed4f6ce1e6c2a6479", +"snowflake": "4ea9204c46e0ec4e3a94f672", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "9917d6c8c04307304904313b", +"None": "9917d6c8c04307304904313b", +"bigquery": "9917d6c8c04307304904313b", +"databricks": "9917d6c8c04307304904313b", +"duckdb": "9917d6c8c04307304904313b", +"generic": "9917d6c8c04307304904313b", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "9917d6c8c04307304904313b", +"postgres": "9917d6c8c04307304904313b", +"postgresql": "9917d6c8c04307304904313b", +"snowflake": "9917d6c8c04307304904313b", +"tsql": "9917d6c8c04307304904313b" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "0712c33a016f7dd6a37ee011", +"None": "0712c33a016f7dd6a37ee011", +"bigquery": "0712c33a016f7dd6a37ee011", +"databricks": "0712c33a016f7dd6a37ee011", +"duckdb": "0712c33a016f7dd6a37ee011", +"generic": "0712c33a016f7dd6a37ee011", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0f3f17e0fe13c04b84ded556", +"postgres": "0712c33a016f7dd6a37ee011", +"postgresql": "0712c33a016f7dd6a37ee011", +"snowflake": "0712c33a016f7dd6a37ee011", +"tsql": "0712c33a016f7dd6a37ee011" +} +], +[ +"type", +{ +"DuckDB": "fe23594c781e79cbb0c8927a", +"None": "fe23594c781e79cbb0c8927a", +"bigquery": "fe23594c781e79cbb0c8927a", +"databricks": "fe23594c781e79cbb0c8927a", +"duckdb": "fe23594c781e79cbb0c8927a", +"generic": "fe23594c781e79cbb0c8927a", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fe23594c781e79cbb0c8927a", +"postgres": "fe23594c781e79cbb0c8927a", +"postgresql": "fe23594c781e79cbb0c8927a", +"snowflake": "38b2ec94bf02be40007ce525", +"tsql": "fe23594c781e79cbb0c8927a" +} +], +[ +"type", +{ +"DuckDB": "7fa8f60977ab1aa305af9d4b", +"None": "8c6ca3f77bf44837655a903e", +"bigquery": "89138ea06a52edf9b7991dd8", +"databricks": "3aa50c38af078e9f02f5a1c8", +"duckdb": "7fa8f60977ab1aa305af9d4b", +"generic": "8c6ca3f77bf44837655a903e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "8c6ca3f77bf44837655a903e", +"postgres": "8c6ca3f77bf44837655a903e", +"postgresql": "8c6ca3f77bf44837655a903e", +"snowflake": "05188197e056965f895710b0", +"tsql": "8c6ca3f77bf44837655a903e" +} +], +[ +"type", +{ +"DuckDB": "ee47e1424cb01deb0cf51760", +"None": "ee47e1424cb01deb0cf51760", +"bigquery": "ee47e1424cb01deb0cf51760", +"databricks": "ee47e1424cb01deb0cf51760", +"duckdb": "ee47e1424cb01deb0cf51760", +"generic": "ee47e1424cb01deb0cf51760", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ee47e1424cb01deb0cf51760", +"postgres": "ee47e1424cb01deb0cf51760", +"postgresql": "ee47e1424cb01deb0cf51760", +"snowflake": "ee47e1424cb01deb0cf51760", +"tsql": "ee47e1424cb01deb0cf51760" +} +], +[ +"type", +{ +"DuckDB": "49fdedaa5356fcccaa6f2f38", +"None": "49fdedaa5356fcccaa6f2f38", +"bigquery": "49fdedaa5356fcccaa6f2f38", +"databricks": "49fdedaa5356fcccaa6f2f38", +"duckdb": "49fdedaa5356fcccaa6f2f38", +"generic": "49fdedaa5356fcccaa6f2f38", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "49fdedaa5356fcccaa6f2f38", +"postgres": "49fdedaa5356fcccaa6f2f38", +"postgresql": "49fdedaa5356fcccaa6f2f38", +"snowflake": "49fdedaa5356fcccaa6f2f38", +"tsql": "49fdedaa5356fcccaa6f2f38" +} +], +[ +"type", +{ +"DuckDB": "70fb4728c3adcf6dbe4975d5", +"None": "70fb4728c3adcf6dbe4975d5", +"bigquery": "70fb4728c3adcf6dbe4975d5", +"databricks": "70fb4728c3adcf6dbe4975d5", +"duckdb": "70fb4728c3adcf6dbe4975d5", +"generic": "70fb4728c3adcf6dbe4975d5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "70fb4728c3adcf6dbe4975d5", +"postgres": "70fb4728c3adcf6dbe4975d5", +"postgresql": "70fb4728c3adcf6dbe4975d5", +"snowflake": "70fb4728c3adcf6dbe4975d5", +"tsql": "70fb4728c3adcf6dbe4975d5" +} +], +[ +"type", +{ +"DuckDB": "19bdd92ea0cc5fb028657510", +"None": "b8321619b794db091c9e96a1", +"bigquery": "b8321619b794db091c9e96a1", +"databricks": "1e75e5227bea0ae294ac7af1", +"duckdb": "19bdd92ea0cc5fb028657510", +"generic": "b8321619b794db091c9e96a1", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "b8321619b794db091c9e96a1", +"postgres": "b8321619b794db091c9e96a1", +"postgresql": "b8321619b794db091c9e96a1", +"snowflake": "b8321619b794db091c9e96a1", +"tsql": "b8321619b794db091c9e96a1" +} +], +[ +"type", +{ +"DuckDB": "9f79ba9960d200f49192fa87", +"None": "46e6767a9ba2cc5fbfdc02d0", +"bigquery": "135a912ea74f76a35d9d0632", +"databricks": "46e6767a9ba2cc5fbfdc02d0", +"duckdb": "9f79ba9960d200f49192fa87", +"generic": "46e6767a9ba2cc5fbfdc02d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "6c03a3450ea4742ee7396e3c", +"postgres": "9f79ba9960d200f49192fa87", +"postgresql": "9f79ba9960d200f49192fa87", +"snowflake": "72c40a5d469c974c1952c3c2", +"tsql": "d1dc9a140dbe2eb1874c6ad7" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "305aaf0b95cee629dd46380c", +"None": "d61c83ef8bfdf043fa672cb5", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "d61c83ef8bfdf043fa672cb5", +"duckdb": "305aaf0b95cee629dd46380c", +"generic": "d61c83ef8bfdf043fa672cb5", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "d61c83ef8bfdf043fa672cb5", +"postgres": "d61c83ef8bfdf043fa672cb5", +"postgresql": "d61c83ef8bfdf043fa672cb5", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "d61c83ef8bfdf043fa672cb5" +} +], +[ +"type", +{ +"DuckDB": "0dc0164f07551711f23943eb", +"None": "0dc0164f07551711f23943eb", +"bigquery": "0dc0164f07551711f23943eb", +"databricks": "0dc0164f07551711f23943eb", +"duckdb": "0dc0164f07551711f23943eb", +"generic": "0dc0164f07551711f23943eb", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0dc0164f07551711f23943eb", +"postgres": "0dc0164f07551711f23943eb", +"postgresql": "0dc0164f07551711f23943eb", +"snowflake": "0dc0164f07551711f23943eb", +"tsql": "0dc0164f07551711f23943eb" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +], +[ +"type", +{ +"DuckDB": "0f799a95a4f5ef502897f02c", +"None": "0f799a95a4f5ef502897f02c", +"bigquery": "b1b23029bedaa16e2a6f8516", +"databricks": "0f799a95a4f5ef502897f02c", +"duckdb": "0f799a95a4f5ef502897f02c", +"generic": "0f799a95a4f5ef502897f02c", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0f799a95a4f5ef502897f02c", +"postgres": "0f799a95a4f5ef502897f02c", +"postgresql": "0f799a95a4f5ef502897f02c", +"snowflake": "0f799a95a4f5ef502897f02c", +"tsql": "0f799a95a4f5ef502897f02c" +} +], +[ +"type", +{ +"DuckDB": "2bfa352725f9d8a91250f75e", +"None": "2bfa352725f9d8a91250f75e", +"bigquery": "2bfa352725f9d8a91250f75e", +"databricks": "2bfa352725f9d8a91250f75e", +"duckdb": "2bfa352725f9d8a91250f75e", +"generic": "2bfa352725f9d8a91250f75e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2bfa352725f9d8a91250f75e", +"postgres": "2bfa352725f9d8a91250f75e", +"postgresql": "2bfa352725f9d8a91250f75e", +"snowflake": "2bfa352725f9d8a91250f75e", +"tsql": "2bfa352725f9d8a91250f75e" +} +], +[ +"type", +{ +"DuckDB": "bda0c90e573c6a01b55627e2", +"None": "bda0c90e573c6a01b55627e2", +"bigquery": "bda0c90e573c6a01b55627e2", +"databricks": "bda0c90e573c6a01b55627e2", +"duckdb": "bda0c90e573c6a01b55627e2", +"generic": "bda0c90e573c6a01b55627e2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "bda0c90e573c6a01b55627e2", +"postgres": "bda0c90e573c6a01b55627e2", +"postgresql": "bda0c90e573c6a01b55627e2", +"snowflake": "bda0c90e573c6a01b55627e2", +"tsql": "bda0c90e573c6a01b55627e2" +} +], +[ +"type", +{ +"DuckDB": "2705f19942a4194a72576911", +"None": "2705f19942a4194a72576911", +"bigquery": "6f763a99e9c69109526b40c0", +"databricks": "2705f19942a4194a72576911", +"duckdb": "2705f19942a4194a72576911", +"generic": "2705f19942a4194a72576911", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "2705f19942a4194a72576911", +"postgres": "2705f19942a4194a72576911", +"postgresql": "2705f19942a4194a72576911", +"snowflake": "2705f19942a4194a72576911", +"tsql": "2705f19942a4194a72576911" +} +], +[ +"type", +{ +"DuckDB": "558da38ec4d9d5d6c72f60cf", +"None": "558da38ec4d9d5d6c72f60cf", +"bigquery": "e6ffdece2977be55da73b59a", +"databricks": "558da38ec4d9d5d6c72f60cf", +"duckdb": "558da38ec4d9d5d6c72f60cf", +"generic": "558da38ec4d9d5d6c72f60cf", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "558da38ec4d9d5d6c72f60cf", +"postgres": "558da38ec4d9d5d6c72f60cf", +"postgresql": "558da38ec4d9d5d6c72f60cf", +"snowflake": "558da38ec4d9d5d6c72f60cf", +"tsql": "558da38ec4d9d5d6c72f60cf" +} +], +[ +"type", +{ +"DuckDB": "314ed506f24e92300a425780", +"None": "314ed506f24e92300a425780", +"bigquery": "314ed506f24e92300a425780", +"databricks": "314ed506f24e92300a425780", +"duckdb": "314ed506f24e92300a425780", +"generic": "314ed506f24e92300a425780", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "314ed506f24e92300a425780", +"postgres": "314ed506f24e92300a425780", +"postgresql": "314ed506f24e92300a425780", +"snowflake": "314ed506f24e92300a425780", +"tsql": "314ed506f24e92300a425780" +} +], +[ +"type", +{ +"DuckDB": "0b8ad171901cc2b7725617da", +"None": "0b8ad171901cc2b7725617da", +"bigquery": "0b8ad171901cc2b7725617da", +"databricks": "0b8ad171901cc2b7725617da", +"duckdb": "0b8ad171901cc2b7725617da", +"generic": "0b8ad171901cc2b7725617da", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0b8ad171901cc2b7725617da", +"postgres": "0b8ad171901cc2b7725617da", +"postgresql": "0b8ad171901cc2b7725617da", +"snowflake": "0b8ad171901cc2b7725617da", +"tsql": "0b8ad171901cc2b7725617da" +} +], +[ +"type", +{ +"DuckDB": "363159f251e264604338e690", +"None": "363159f251e264604338e690", +"bigquery": "363159f251e264604338e690", +"databricks": "363159f251e264604338e690", +"duckdb": "363159f251e264604338e690", +"generic": "363159f251e264604338e690", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "363159f251e264604338e690", +"postgres": "363159f251e264604338e690", +"postgresql": "363159f251e264604338e690", +"snowflake": "363159f251e264604338e690", +"tsql": "363159f251e264604338e690" +} +], +[ +"type", +{ +"DuckDB": "1d8b945fd793ecdcacb90c21", +"None": "1d8b945fd793ecdcacb90c21", +"bigquery": "68d2179839f17f0eb289d1aa", +"databricks": "e3bc1b8bad22d8104417b00f", +"duckdb": "1d8b945fd793ecdcacb90c21", +"generic": "1d8b945fd793ecdcacb90c21", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1d8b945fd793ecdcacb90c21", +"postgres": "3efe8bb57c98fcbe74096b3c", +"postgresql": "3efe8bb57c98fcbe74096b3c", +"snowflake": "1d8b945fd793ecdcacb90c21", +"tsql": "1d8b945fd793ecdcacb90c21" +} +], +[ +"type", +{ +"DuckDB": "52c81331cf7cf6b56395e239", +"None": "52c81331cf7cf6b56395e239", +"bigquery": "52c81331cf7cf6b56395e239", +"databricks": "52c81331cf7cf6b56395e239", +"duckdb": "52c81331cf7cf6b56395e239", +"generic": "52c81331cf7cf6b56395e239", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "52c81331cf7cf6b56395e239", +"postgres": "52c81331cf7cf6b56395e239", +"postgresql": "52c81331cf7cf6b56395e239", +"snowflake": "52c81331cf7cf6b56395e239", +"tsql": "52c81331cf7cf6b56395e239" +} +], +[ +"type", +{ +"DuckDB": "ebc821a95b9286e22c2b26d0", +"None": "ebc821a95b9286e22c2b26d0", +"bigquery": "19a1a80ffc06169f32694de8", +"databricks": "ebc821a95b9286e22c2b26d0", +"duckdb": "ebc821a95b9286e22c2b26d0", +"generic": "ebc821a95b9286e22c2b26d0", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "ebc821a95b9286e22c2b26d0", +"postgres": "ebc821a95b9286e22c2b26d0", +"postgresql": "ebc821a95b9286e22c2b26d0", +"snowflake": "ebc821a95b9286e22c2b26d0", +"tsql": "ebc821a95b9286e22c2b26d0" +} +], +[ +"type", +{ +"DuckDB": "85569b6cabaea71507846025", +"None": "85569b6cabaea71507846025", +"bigquery": "85569b6cabaea71507846025", +"databricks": "85569b6cabaea71507846025", +"duckdb": "85569b6cabaea71507846025", +"generic": "85569b6cabaea71507846025", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "85569b6cabaea71507846025", +"postgres": "85569b6cabaea71507846025", +"postgresql": "85569b6cabaea71507846025", +"snowflake": "85569b6cabaea71507846025", +"tsql": "85569b6cabaea71507846025" +} +], +[ +"type", +{ +"DuckDB": "7f7363467010768b3de8fe05", +"None": "7f7363467010768b3de8fe05", +"bigquery": "7f7363467010768b3de8fe05", +"databricks": "7f7363467010768b3de8fe05", +"duckdb": "7f7363467010768b3de8fe05", +"generic": "7f7363467010768b3de8fe05", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "7f7363467010768b3de8fe05", +"postgres": "7f7363467010768b3de8fe05", +"postgresql": "7f7363467010768b3de8fe05", +"snowflake": "7f7363467010768b3de8fe05", +"tsql": "7f7363467010768b3de8fe05" +} +], +[ +"type", +{ +"DuckDB": "cc5643d29bc94cb1374704e9", +"None": "cc5643d29bc94cb1374704e9", +"bigquery": "cc5643d29bc94cb1374704e9", +"databricks": "cc5643d29bc94cb1374704e9", +"duckdb": "cc5643d29bc94cb1374704e9", +"generic": "cc5643d29bc94cb1374704e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "cc5643d29bc94cb1374704e9", +"postgres": "cc5643d29bc94cb1374704e9", +"postgresql": "cc5643d29bc94cb1374704e9", +"snowflake": "cc5643d29bc94cb1374704e9", +"tsql": "cc5643d29bc94cb1374704e9" +} +], +[ +"type", +{ +"DuckDB": "c50a316d091c821866e4bfba", +"None": "c50a316d091c821866e4bfba", +"bigquery": "c50a316d091c821866e4bfba", +"databricks": "c50a316d091c821866e4bfba", +"duckdb": "c50a316d091c821866e4bfba", +"generic": "c50a316d091c821866e4bfba", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c50a316d091c821866e4bfba", +"postgres": "c50a316d091c821866e4bfba", +"postgresql": "c50a316d091c821866e4bfba", +"snowflake": "c50a316d091c821866e4bfba", +"tsql": "c50a316d091c821866e4bfba" +} +], +[ +"type", +{ +"DuckDB": "c91ea1fc2032c6f196132d1e", +"None": "c91ea1fc2032c6f196132d1e", +"bigquery": "c91ea1fc2032c6f196132d1e", +"databricks": "c91ea1fc2032c6f196132d1e", +"duckdb": "c91ea1fc2032c6f196132d1e", +"generic": "c91ea1fc2032c6f196132d1e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "c91ea1fc2032c6f196132d1e", +"postgres": "c91ea1fc2032c6f196132d1e", +"postgresql": "c91ea1fc2032c6f196132d1e", +"snowflake": "c91ea1fc2032c6f196132d1e", +"tsql": "c91ea1fc2032c6f196132d1e" +} +], +[ +"type", +{ +"DuckDB": "87496ad15619ba3d233601ad", +"None": "87496ad15619ba3d233601ad", +"bigquery": "87496ad15619ba3d233601ad", +"databricks": "5878645b159a4a60df8c2681", +"duckdb": "87496ad15619ba3d233601ad", +"generic": "87496ad15619ba3d233601ad", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "675e09f896465856e2673811", +"postgres": "87496ad15619ba3d233601ad", +"postgresql": "87496ad15619ba3d233601ad", +"snowflake": "87496ad15619ba3d233601ad", +"tsql": "87496ad15619ba3d233601ad" +} +], +[ +"type", +{ +"DuckDB": "23f70cb2333740d79361d52e", +"None": "23f70cb2333740d79361d52e", +"bigquery": "816384049477221b0a4a5b89", +"databricks": "23f70cb2333740d79361d52e", +"duckdb": "23f70cb2333740d79361d52e", +"generic": "23f70cb2333740d79361d52e", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "23f70cb2333740d79361d52e", +"postgres": "23f70cb2333740d79361d52e", +"postgresql": "23f70cb2333740d79361d52e", +"snowflake": "23f70cb2333740d79361d52e", +"tsql": "23f70cb2333740d79361d52e" +} +], +[ +"type", +{ +"DuckDB": "1eb7fb4e8cf75773c5573562", +"None": "1eb7fb4e8cf75773c5573562", +"bigquery": "c9b810639795a7bfab91b98d", +"databricks": "1eb7fb4e8cf75773c5573562", +"duckdb": "1eb7fb4e8cf75773c5573562", +"generic": "1eb7fb4e8cf75773c5573562", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "1eb7fb4e8cf75773c5573562", +"postgres": "1eb7fb4e8cf75773c5573562", +"postgresql": "1eb7fb4e8cf75773c5573562", +"snowflake": "97a20344d811fb77c7690863", +"tsql": "1eb7fb4e8cf75773c5573562" +} +], +[ +"type", +{ +"DuckDB": "300e2b67af892b463d85f9cd", +"None": "300e2b67af892b463d85f9cd", +"bigquery": "300e2b67af892b463d85f9cd", +"databricks": "300e2b67af892b463d85f9cd", +"duckdb": "300e2b67af892b463d85f9cd", +"generic": "300e2b67af892b463d85f9cd", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "300e2b67af892b463d85f9cd", +"postgres": "300e2b67af892b463d85f9cd", +"postgresql": "300e2b67af892b463d85f9cd", +"snowflake": "300e2b67af892b463d85f9cd", +"tsql": "300e2b67af892b463d85f9cd" +} +], +[ +"type", +{ +"DuckDB": "669084d1ea704ae5b8ee05c2", +"None": "669084d1ea704ae5b8ee05c2", +"bigquery": "669084d1ea704ae5b8ee05c2", +"databricks": "669084d1ea704ae5b8ee05c2", +"duckdb": "669084d1ea704ae5b8ee05c2", +"generic": "669084d1ea704ae5b8ee05c2", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "669084d1ea704ae5b8ee05c2", +"postgres": "669084d1ea704ae5b8ee05c2", +"postgresql": "669084d1ea704ae5b8ee05c2", +"snowflake": "669084d1ea704ae5b8ee05c2", +"tsql": "669084d1ea704ae5b8ee05c2" +} +], +[ +"type", +{ +"DuckDB": "08736485547ba339930697e9", +"None": "08736485547ba339930697e9", +"bigquery": "08736485547ba339930697e9", +"databricks": "08736485547ba339930697e9", +"duckdb": "08736485547ba339930697e9", +"generic": "08736485547ba339930697e9", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "08736485547ba339930697e9", +"postgres": "08736485547ba339930697e9", +"postgresql": "08736485547ba339930697e9", +"snowflake": "08736485547ba339930697e9", +"tsql": "08736485547ba339930697e9" +} +], +[ +"type", +{ +"DuckDB": "0c4610f182f18bf41d306e01", +"None": "0c4610f182f18bf41d306e01", +"bigquery": "0c4610f182f18bf41d306e01", +"databricks": "0c4610f182f18bf41d306e01", +"duckdb": "0c4610f182f18bf41d306e01", +"generic": "0c4610f182f18bf41d306e01", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "0c4610f182f18bf41d306e01", +"postgres": "0c4610f182f18bf41d306e01", +"postgresql": "0c4610f182f18bf41d306e01", +"snowflake": "0c4610f182f18bf41d306e01", +"tsql": "0c4610f182f18bf41d306e01" +} +], +[ +"type", +{ +"DuckDB": "fad6caec1e65529a6edc32df", +"None": "fad6caec1e65529a6edc32df", +"bigquery": "fad6caec1e65529a6edc32df", +"databricks": "fad6caec1e65529a6edc32df", +"duckdb": "fad6caec1e65529a6edc32df", +"generic": "fad6caec1e65529a6edc32df", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "fad6caec1e65529a6edc32df", +"postgres": "fad6caec1e65529a6edc32df", +"postgresql": "fad6caec1e65529a6edc32df", +"snowflake": "fad6caec1e65529a6edc32df", +"tsql": "fad6caec1e65529a6edc32df" +} +], +[ +"type", +{ +"DuckDB": "a428302e4848ffe877284c84", +"None": "a428302e4848ffe877284c84", +"bigquery": "a428302e4848ffe877284c84", +"databricks": "a428302e4848ffe877284c84", +"duckdb": "a428302e4848ffe877284c84", +"generic": "a428302e4848ffe877284c84", +"motherduck": "901c5133ed47d4769286e860", +"mysql": "a428302e4848ffe877284c84", +"postgres": "a428302e4848ffe877284c84", +"postgresql": "a428302e4848ffe877284c84", +"snowflake": "652ff8b21f638eac8fdc46c5", +"tsql": "a428302e4848ffe877284c84" +} +] +] diff --git a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py index 723dfd1197..9ea1033a45 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from sqlbuild.adapter.contract.models import NormalizedType +from sqlbuild.adapter.contract.types import TypeFamily @dataclass(frozen=True) @@ -45,7 +46,7 @@ class DeepTypeTestCase: description: str type_sql: str - expected_family: str + expected_family: TypeFamily @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py index 3e4f247b32..874eb28c69 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py @@ -23,6 +23,7 @@ UnknownDialectTypeTestCase, ) from tests.integration.src.sqlbuild.adapter.type_system.helpers import ( + DIALECTS, TypeOutcome, generated_type, is_normalized, @@ -30,6 +31,13 @@ type_outcome, type_outcomes, ) +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) @pytest.mark.parametrize( @@ -53,7 +61,23 @@ def test_given_generated_types_when_normalizing_then_every_known_dialect_answers type_strings: list[str] = [generated_type(rng=rng) for _ in range(test_case.count)] outcomes: list[TypeOutcome] = type_outcomes(type_strings=type_strings) normalized: list[TypeOutcome] = list(filter(is_normalized, outcomes)) + golden: list[GoldenEntry] = [ + golden_entry( + "type", + { + str(outcome.dialect): (outcome.normalized, bool(outcome.logged_errors)) + for outcome in outcomes[index * len(DIALECTS) : (index + 1) * len(DIALECTS)] + }, + ) + for index in range(len(type_strings)) + ] + assert ( + golden_differences( + read_golden(golden_name("types_generated", test_case.description)), golden + ) + == [] + ) assert ( len(normalized) >= test_case.expected_minimum_normalized, sum(map(logged_parse_error, normalized)) >= test_case.expected_minimum_parse_errors, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index dbb3390ebe..fe5228eae7 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -100,3 +100,13 @@ class AdapterRuleCallbackTestCase: model_count: int raised: type[Exception] | None expected_minimum_calls: int + + +@dataclass(frozen=True) +class SharedQueryOutputTestCase: + """Models with equal query shapes over different inputs, and each model's own outputs.""" + + description: str + models: dict[str, str] + expected_lineage: tuple[tuple[str, str, str], ...] = () + expected_findings: tuple[tuple[str, str], ...] = () diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 5a37b7dd59..0aac71deab 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -53,6 +53,7 @@ CompactLineageFacts, CompileAdapterContext, CompiledLineageColumnFact, + CompiledModel, CompileProjectInputs, DynamicColumnContractProof, InferredColumn, @@ -62,6 +63,7 @@ from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily from sqlbuild.sql_values.types import CollectionRendering +from tests.integration.src.sqlbuild.compiler.golden_views import GoldenEntry, golden_entry _PROJECT_TOML: str = ( 'name = "orders_analysis"\nadapter = "duckdb"\n\n[connection]\ndatabase = "orders.duckdb"\n' @@ -501,7 +503,7 @@ def started_sessions(*, monkeypatch: pytest.MonkeyPatch) -> list[Any]: started: list[Any] = [] original: Callable[..., Any] = native_module.start_model_analysis_session - def start(catalog: object, request: tuple[object, ...], *options: object) -> Any: + def start(catalog: object, request: tuple[object, ...], *options: Any) -> Any: started.append(original(catalog, request, *options)) return started[-1] @@ -571,6 +573,7 @@ class NativeAnalysisRuns: native_enrichments: int = 0 native_column_objects: int = 0 native_column_values: int = 0 + golden: list[GoldenEntry] = field(default_factory=list) def analyse_natively( @@ -586,6 +589,7 @@ def analyse_natively( Every analysis, catalog change, session and standalone dynamic pivot proof, and expression shape must not depend on the analysis store, on the session, or on Python's shape inference. + `runs.golden` gathers the entries the Python oracle recorded for the same seam calls. """ original_analysis: Callable[..., tuple[dict[str, ModelSqlAnalysis], Any]] = ( @@ -601,7 +605,7 @@ def analysed_three_ways( ) cached_views: list[object] = [] for _ in range(2): - catalog: Any = profile.binding_catalog.with_relations({}) + catalog: Any = cast(Any, profile.binding_catalog).with_relations({}) cached: NativeModelAnalyses = analyze_native_model_sql( request=replace( request, @@ -618,6 +622,8 @@ def analysed_three_ways( runs.native_column_objects += len(set(map(id, columns))) runs.native_column_values += len(set(columns)) runs.analysed_models += len(analyses) + runs.golden.append(golden_entry("models", _analysis_views(analyses))) + runs.golden.append(golden_entry("catalog", _catalog_view(profile.binding_catalog))) for index, view in enumerate(cached_views): _append(runs, f"model analyses, cached run {index}", uncached_view, view) _compare_proofs(request=request, analyses=analyses, session=session, runs=runs) @@ -635,6 +641,7 @@ def shapes_by_both( expressions=expressions, profile=profile ) runs.expression_shapes += sum(shape is not None for shape in python) + runs.golden.append(golden_entry("shapes", (native, native_catalog.expression_shapes))) _append( runs, "expression shapes", @@ -651,8 +658,16 @@ def counted_native_enrichment(rows: list[Any]) -> tuple[CompiledLineageColumnFac runs.native_enrichments += 1 return native_lineage_facts(rows) + assembled_model: Callable[..., CompiledModel] = project_assembly._assemble_compiled_model + + def recorded_model(*arguments: Any, **keywords: Any) -> CompiledModel: + model: CompiledModel = assembled_model(*arguments, **keywords) + runs.golden.append(golden_entry("proof", model.dynamic_column_contract)) + return model + with monkeypatch.context() as patch: patch.setattr(native_model_analysis, "lineage_facts", counted_native_enrichment) + patch.setattr(project_assembly, "_assemble_compiled_model", recorded_model) patch.setattr(project_assembly, "analyze_model_sql", analysed_three_ways) patch.setattr(project_assembly, "expression_source_shapes_by_engine", shapes_by_both) with suppress(CompileInputError): diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index fbb25be238..c05d76eee1 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -54,6 +54,11 @@ shared_analysis_files, started_sessions, ) +from tests.integration.src.sqlbuild.compiler.golden_views import ( + golden_differences, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.helpers import mismatches _DIALECT_PROFILES: tuple[ExpressionInferenceProfile, ...] = tuple( @@ -210,6 +215,12 @@ def test_given_generated_projects_when_analysing_natively_then_cached_and_proven kinds: Counter[str] = deferral_kinds(record_dir) kinds.pop("analysis_session:adapter_nullability_callback", None) + assert ( + golden_differences( + read_golden(golden_name("analysis_generated", test_case.description)), runs.golden + ) + == [] + ) assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] assert kinds == Counter() assert runs.analysed_models >= test_case.expected_minimum_native @@ -268,6 +279,12 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_cache uncached: list[Any] = sessions[2::3] shared: int = sum(session.sharing[0] for session in uncached) + assert ( + golden_differences( + read_golden(golden_name("analysis_shared", test_case.description)), runs.golden + ) + == [] + ) assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] assert ( runs.analysed_models, @@ -324,6 +341,12 @@ def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_ca for dialect, lineage_mode in product(test_case.dialects, test_case.lineage_modes) ] + assert ( + golden_differences( + read_golden(golden_name("analysis_cyclic", test_case.description)), runs.golden + ) + == [] + ) assert mismatches(inputs=runs.names, expected=runs.uncached, actual=runs.cached) == [] assert ( runs.analysed_models, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py new file mode 100644 index 0000000000..a2755c5192 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py @@ -0,0 +1,225 @@ +"""Models whose analysis queries are equal in shape keep their own diagnostics and lineage. + +Explicit expected outputs carried over from the deleted Python shared-binding tests. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +import pytest + +from sqlbuild.cli.commands.main.entrypoint.entry import main +from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + SharedQueryOutputTestCase, +) + +_UPSTREAM_SQL: str = ( + "MODEL (description 'Test model.', materialized view); SELECT 1 AS id, 2.5 AS amount" +) +_HEADER: str = 'MODEL (description "Test model.", materialized view); ' +_STUB_CTE: str = ( + "MODEL (description 'Test model.', materialized view); WITH {name} AS " + '(SELECT CAST(2 AS VARCHAR) AS id) SELECT id FROM __ref("{upstream}")' +) +_MISSING: str = _HEADER + 'SELECT id FROM __ref("{upstream}") WHERE missing > 0' + + +def _write_project(*, project_dir: Path, models: dict[str, str]) -> None: + _ = (project_dir / "sqlbuild_project.toml").write_text( + 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n', encoding="utf-8" + ) + model_dir: Path = project_dir / "models" + model_dir.mkdir() + for name, sql in {"orders": _UPSTREAM_SQL, "customers": _UPSTREAM_SQL, **models}.items(): + _ = (model_dir / f"{name}.sql").write_text(sql, encoding="utf-8") + + +def _upstream_sources( + *, project_dir: Path, column: str, mode: str, capsys: pytest.CaptureFixture[str] +) -> tuple[int, list[str]]: + code: int = main( + [ + "--project-dir", + str(project_dir), + "lineage", + column, + "--format", + "json", + "--direction", + "upstream", + "--depth", + "1", + "--mode", + mode, + ] + ) + trace: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["trace"] + return code, [step["source"]["resource_name"] for step in trace] + + +@pytest.mark.parametrize( + "test_case", + [ + SharedQueryOutputTestCase( + description="identical shapes over different inputs", + models={ + "orders_summary": _HEADER + 'SELECT id, amount FROM __ref("orders")', + "customers_summary": _HEADER + 'SELECT id, amount FROM __ref("customers")', + }, + expected_lineage=( + ("orders_summary.id", "orders", "rich"), + ("customers_summary.id", "customers", "rich"), + ("orders_summary.amount", "orders", "fast"), + ("customers_summary.amount", "customers", "fast"), + ), + ), + SharedQueryOutputTestCase( + description="a relation name used as a qualifier", + models={ + "orders_summary": _HEADER + 'SELECT orders.id FROM __ref("orders") AS orders', + "customers_summary": ( + _HEADER + 'SELECT customers.id FROM __ref("customers") AS customers' + ), + }, + expected_lineage=( + ("orders_summary.id", "orders", "rich"), + ("customers_summary.id", "customers", "rich"), + ), + ), + SharedQueryOutputTestCase( + description="an authored lowercase stub-named CTE", + models={ + "orders_summary": _STUB_CTE.format( + name="__sqlbuild_project_input_0", upstream="orders" + ), + "customers_summary": _STUB_CTE.format( + name="__sqlbuild_project_input_0", upstream="customers" + ), + "next_orders": _HEADER + 'SELECT id + 1 AS next_id FROM __ref("orders_summary")', + }, + expected_lineage=( + ("orders_summary.id", "orders", "rich"), + ("customers_summary.id", "customers", "rich"), + ), + ), + SharedQueryOutputTestCase( + description="an authored uppercase stub-named CTE", + models={ + "orders_summary": _STUB_CTE.format( + name="__SQLBUILD_PROJECT_INPUT_0", upstream="orders" + ), + "customers_summary": _STUB_CTE.format( + name="__SQLBUILD_PROJECT_INPUT_0", upstream="customers" + ), + "next_orders": _HEADER + 'SELECT id + 1 AS next_id FROM __ref("orders_summary")', + }, + expected_lineage=( + ("orders_summary.id", "orders", "rich"), + ("customers_summary.id", "customers", "rich"), + ), + ), + SharedQueryOutputTestCase( + description="later-wave members of equal shape", + models={ + "orders_summary": _HEADER + 'SELECT id, amount FROM __ref("orders")', + "customers_summary": _HEADER + 'SELECT id, amount FROM __ref("customers")', + "orders_rollup": _HEADER + 'SELECT id, amount FROM __ref("orders_summary")', + "customers_rollup": (_HEADER + 'SELECT id, amount FROM __ref("customers_summary")'), + }, + expected_lineage=( + ("orders_rollup.amount", "orders_summary", "rich"), + ("customers_rollup.amount", "customers_summary", "rich"), + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_equal_shapes_when_compiling_then_each_model_keeps_its_own_lineage( + test_case: SharedQueryOutputTestCase, + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + _write_project(project_dir=tmp_path, models=test_case.models) + + compiled: int = main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) + diagnostics: list[object] = json.loads(capsys.readouterr().out)["diagnostics"] + traces: list[tuple[int, list[str]]] = [ + _upstream_sources(project_dir=tmp_path, column=column, mode=mode, capsys=capsys) + for column, _, mode in test_case.expected_lineage + ] + + assert (compiled, diagnostics) == (0, []) + assert traces == [(0, [upstream]) for _, upstream, _ in test_case.expected_lineage] + + +@pytest.mark.parametrize( + "test_case", + [ + SharedQueryOutputTestCase( + description="findings after the relation", + models={ + "orders_summary": _MISSING.format(upstream="orders"), + "customers_summary": _MISSING.format(upstream="customers"), + }, + expected_findings=(("orders_summary", "orders"), ("customers_summary", "customers")), + ), + SharedQueryOutputTestCase( + description="a later-wave member of a group with findings", + models={ + "orders_summary": _MISSING.format(upstream="orders"), + "customers_summary": _MISSING.format(upstream="customers"), + "orders_mid": _HEADER + 'SELECT id, amount FROM __ref("orders")', + "orders_late": _MISSING.format(upstream="orders_mid"), + }, + expected_findings=( + ("orders_summary", "orders"), + ("customers_summary", "customers"), + ("orders_late", "orders_mid"), + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_equal_shapes_with_findings_when_compiling_then_reports_each_model_exactly( + test_case: SharedQueryOutputTestCase, + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + _write_project(project_dir=tmp_path, models=test_case.models) + + compiled: int = main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) + diagnostics: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["diagnostics"] + findings: list[tuple[str, str, str, int, int]] = sorted( + ( + diagnostic["resource_name"], + diagnostic["code"], + diagnostic["message"], + diagnostic["location"]["column"], + diagnostic["location"]["end_column"], + ) + for diagnostic in diagnostics + ) + columns: dict[str, int] = { + model: sql.index("missing") + 1 + for model, sql in test_case.models.items() + if "missing" in sql + } + + assert compiled == 1 + assert findings == sorted( + ( + model, + "B002", + f"Unknown column 'missing' in {upstream}", + columns[model], + columns[model] + len("missing"), + ) + for model, upstream in test_case.expected_findings + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py index 653e5eb3d4..4d735dffe3 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/helpers.py @@ -284,6 +284,26 @@ def contract_diagnostics(*, project: CompiledProject, dialect: str | None) -> Co return evaluate_model_contracts(project=project, dialect=dialect).diagnostics +def diagnostic_view(*, diagnostics: ContractView, project_dir: Path) -> str: + """Every field of the diagnostics, in order, with the project directory masked.""" + + return repr( + [ + ( + diagnostic.code, + diagnostic.severity, + diagnostic.resource_name, + diagnostic.column_name, + diagnostic.message, + diagnostic.location, + diagnostic.related_locations, + diagnostic.help, + ) + for diagnostic in diagnostics + ] + ).replace(str(project_dir), "") + + class NativeContractRecord(NamedTuple): """Native contract outcomes: `native` (evaluated natively), `typed_comparisons` and `native_diagnostics` in `statuses`; codes of natively built diagnostics in `codes`.""" diff --git a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py index 21edb544c2..1d15dee486 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py @@ -17,10 +17,18 @@ NativeContractRecord, compiled_contract_project, contract_diagnostics, + diagnostic_view, perturbed_project, record_native_outcomes, with_declared_type, ) +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) @pytest.mark.parametrize( @@ -46,12 +54,30 @@ def test_given_generated_contracts_when_validating_then_every_contract_family_is ) -> None: rng: random.Random = random.Random(test_case.seed) record: NativeContractRecord = record_native_outcomes(monkeypatch=monkeypatch) - base: CompiledProject = compiled_contract_project(project_dir=tmp_path / "project") + project_dir: Path = tmp_path / "project" + base: CompiledProject = compiled_contract_project(project_dir=project_dir) + golden: list[GoldenEntry] = [] for _ in range(test_case.variants): project: CompiledProject = perturbed_project(project=base, rng=rng) - for dialect in test_case.dialects: - _ = contract_diagnostics(project=project, dialect=dialect) + golden.append( + golden_entry( + "contracts", + { + str(dialect): diagnostic_view( + diagnostics=contract_diagnostics(project=project, dialect=dialect), + project_dir=project_dir, + ) + for dialect in test_case.dialects + }, + ) + ) + assert ( + golden_differences( + read_golden(golden_name("contracts_generated", test_case.description)), golden + ) + == [] + ) assert ( record.statuses["native"] >= test_case.expected_minimum_native, record.statuses["typed_comparisons"] >= test_case.expected_minimum_typed_comparisons, diff --git a/tests/integration/src/sqlbuild/compiler/golden_views.py b/tests/integration/src/sqlbuild/compiler/golden_views.py new file mode 100644 index 0000000000..cb295310d1 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/golden_views.py @@ -0,0 +1,120 @@ +"""Expected outputs recorded once from the pre-port Python implementations, compared by digest. + +Each golden file holds the Python oracle's view of every seam call a test makes, in call order, +as `[kind, digest]` or `[kind, {name: digest}]` entries. A test rebuilds the same entries from +the native result and must match them exactly, so two equal native runs cannot stand in for +the deleted Python oracle. +""" + +from __future__ import annotations + +import dataclasses +import hashlib +import json +import re +from enum import Enum +from pathlib import Path +from typing import cast + +GOLDEN_DIR: Path = Path(__file__).resolve().parents[4] / "goldens" / "python_oracle" + +type GoldenEntry = list[object] + + +def canonical(value: object) -> object: + """A JSON value standing for `value`, equal exactly when the Python values are equal.""" + + if isinstance(value, Enum): + return [type(value).__name__, canonical(value.value)] + if dataclasses.is_dataclass(value) and not isinstance(value, type): + return [ + type(value).__name__, + { + field.name: canonical(getattr(value, field.name)) + for field in dataclasses.fields(value) + }, + ] + if isinstance(value, dict): + return [ + ["key", canonical(key), canonical(item)] + for key, item in sorted( + value.items(), key=lambda pair: json.dumps(canonical(pair[0]), sort_keys=True) + ) + ] + if isinstance(value, list | tuple): + return [type(value).__name__, [canonical(item) for item in value]] + if isinstance(value, frozenset | set): + return ["set", sorted((canonical(item) for item in value), key=json.dumps)] + if value is None or isinstance(value, bool | int | float | str): + return value + return ["repr", repr(value)] + + +def digest(value: object) -> str: + """The SHA-256 of `value`'s canonical JSON, shortened for golden files.""" + + encoded: bytes = json.dumps(canonical(value), sort_keys=True, ensure_ascii=False).encode() + return hashlib.sha256(encoded).hexdigest()[:24] + + +def golden_entry(kind: str, view: object) -> GoldenEntry: + """One seam call's golden entry: a digest per named item for mappings, else one digest.""" + + if isinstance(view, dict): + return [kind, {str(name): digest(item) for name, item in view.items()}] + return [kind, digest(view)] + + +def golden_name(prefix: str, description: str) -> str: + """The golden file name of a test case: its prefix and its description as a slug.""" + + return f"{prefix}_{re.sub(r'[^a-z0-9]+', '_', description.lower()).strip('_')}" + + +def read_golden(name: str) -> list[GoldenEntry]: + """The recorded entries of one golden file.""" + + return json.loads((GOLDEN_DIR / f"{name}.json").read_text(encoding="utf-8")) + + +def write_golden(name: str, entries: list[GoldenEntry]) -> None: + """Record one golden file; used only by the one-off oracle recording.""" + + GOLDEN_DIR.mkdir(parents=True, exist_ok=True) + _ = (GOLDEN_DIR / f"{name}.json").write_text( + json.dumps(entries, indent=0, sort_keys=True) + "\n", encoding="utf-8" + ) + + +def golden_differences( + expected: list[GoldenEntry], actual: list[GoldenEntry] +) -> list[tuple[int, str, object, object]]: + """`(index, kind, expected, actual)` for every entry that differs, names compared one by one.""" + + differences: list[tuple[int, str, object, object]] = [] + for index in range(max(len(expected), len(actual))): + want: GoldenEntry | None = expected[index] if index < len(expected) else None + got: GoldenEntry | None = actual[index] if index < len(actual) else None + if want == got: + continue + want_items: object = want[1] if want is not None else None + got_items: object = got[1] if got is not None else None + if ( + want is not None + and got is not None + and want[0] == got[0] + and isinstance(want_items, dict) + and isinstance(got_items, dict) + ): + want_digests: dict[str, object] = cast(dict[str, object], want_items) + got_digests: dict[str, object] = cast(dict[str, object], got_items) + differences.extend( + (index, f"{want[0]}:{name}", want_digests.get(name), got_digests.get(name)) + for name in sorted({*want_digests, *got_digests}) + if want_digests.get(name) != got_digests.get(name) + ) + continue + differences.append( + (index, str((want or got or ["?"])[0]), want and want[1], got and got[1]) + ) + return differences diff --git a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py index 2ab26f0327..2cf9726902 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py @@ -1,4 +1,4 @@ -"""Native fast column lineage equals Python's on generated projects, model by model.""" +"""Native fast column lineage: Python-oracle goldens, restored projects and internal failures.""" from __future__ import annotations @@ -17,6 +17,13 @@ ) from sqlbuild.compiler.lineage.models import ProjectColumnLineage from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.helpers import mismatches from tests.integration.src.sqlbuild.compiler.lineage._test_types import ( FormerlyDeferredLineageTestCase, @@ -60,6 +67,7 @@ def test_given_generated_projects_when_building_fast_lineage_then_restored_proje rng: random.Random = random.Random(test_case.seed) statuses: Counter[str] = record_native_outcomes(monkeypatch=monkeypatch) differences: list[tuple[object, object, object]] = [] + golden: list[GoldenEntry] = [] for index in range(test_case.count): project: CompiledProject = compiled_project( project_dir=tmp_path / f"project_{index}", @@ -74,6 +82,17 @@ def test_given_generated_projects_when_building_fast_lineage_then_restored_proje names, with_catalog = lineage_views( project=variant, dialect=dialect, model_names=model_names ) + golden.append( + golden_entry( + "lineage", + { + f"{position}:{name}": view + for position, (name, view) in enumerate( + zip(names, with_catalog, strict=True) + ) + }, + ) + ) _, restored = lineage_views( project=replace(variant, binding_catalog=None), dialect=dialect, @@ -83,6 +102,12 @@ def test_given_generated_projects_when_building_fast_lineage_then_restored_proje mismatches(inputs=names, expected=with_catalog, actual=restored) ) + assert ( + golden_differences( + read_golden(golden_name("fast_lineage_generated", test_case.description)), golden + ) + == [] + ) assert differences == [] assert statuses["deferred"] == 0 assert sum(statuses.values()) >= test_case.expected_minimum_native diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_binding_waves.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_binding_waves.py index c4ff500ce8..62d45e3e63 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_binding_waves.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_binding_waves.py @@ -1,14 +1,12 @@ """Dependency-ready models bind against producer outputs in their first analysis.""" import json -from collections.abc import Callable from pathlib import Path from typing import Any import pytest from sqlbuild.cli.commands.main.entrypoint.entry import main -from sqlbuild.compiler.compile._helpers.assembly import project from tests.integration.src.sqlbuild.compiler.pipeline._test_types import NativeCatalogCase @@ -17,11 +15,10 @@ [NativeCatalogCase("closed dependency waves", 'SELECT id FROM __ref("orders")')], ids=lambda case: case.description, ) -def test_given_inferred_producer_when_binding_consumers_then_avoids_deferred_requests_and_invalidates_changed_shapes( +def test_given_inferred_producer_when_binding_consumers_then_binds_and_invalidates_changed_shapes( test_case: NativeCatalogCase, tmp_path: Path, capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, ) -> None: (tmp_path / "sqlbuild_project.toml").write_text( 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n' @@ -38,19 +35,10 @@ def test_given_inferred_producer_when_binding_consumers_then_avoids_deferred_req (tmp_path / "models/report.sql").write_text( "MODEL (description 'Test model report.', materialized view); " + test_case.sql ) - original: Callable[..., Any] = project.get_schema_validations - requests: list[Any] = [] - - def traced(**kwargs: Any) -> Any: - requests.extend(kwargs["requests"]) - return original(**kwargs) - - monkeypatch.setattr(project, "get_schema_validations", traced) args: list[str] = ["--project-dir", str(tmp_path), "compile", "--json"] assert main(args) == 0 cold: dict[str, Any] = json.loads(capsys.readouterr().out) assert tuple(item["code"] for item in cold["diagnostics"]) == test_case.expected_codes - assert requests == [] assert main(args) == 0 warm: dict[str, Any] = json.loads(capsys.readouterr().out) assert warm["resources"] == cold["resources"] @@ -58,7 +46,6 @@ def traced(**kwargs: Any) -> Any: assert main(args) == 1 changed: dict[str, Any] = json.loads(capsys.readouterr().out) assert [item["code"] for item in changed["diagnostics"]] == ["B002"] - assert requests == [] assert main([*args, "--no-cache"]) == 1 oracle: dict[str, Any] = json.loads(capsys.readouterr().out) assert oracle["diagnostics"] == changed["diagnostics"] diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py index bf704a1dc1..ff964c9df2 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py @@ -1,14 +1,12 @@ """Real compiler regressions for binding boundary review findings.""" import json -from collections.abc import Callable from pathlib import Path from typing import Any import pytest from sqlbuild.cli.commands.main.entrypoint.entry import main -from sqlbuild.compiler.compile._helpers.assembly import project from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( IdentifierBindingCase, @@ -134,6 +132,18 @@ def test_given_nested_function_calls_when_compiling_then_native_normalization_do "Postgres quoted mismatch", "postgres", '"Customer"', '"customer"', ("B002",) ), IdentifierBindingCase("Postgres unquoted lowercase", "postgres", "Customer", '"customer"'), + IdentifierBindingCase( + "Postgres unknown column", "postgres", "customer", "missing", ("B002",) + ), + IdentifierBindingCase( + "MotherDuck quoted insensitive", "motherduck", '"Customer"', '"CUSTOMER"' + ), + IdentifierBindingCase( + "MotherDuck unknown column", "motherduck", "customer", "missing", ("B002",) + ), + IdentifierBindingCase( + "SQL Server unknown column", "sqlserver", "customer", "missing", ("B002",) + ), ], ids=lambda case: case.description, ) @@ -141,15 +151,7 @@ def test_given_inferred_output_when_binding_downstream_then_preserves_dialect_id test_case: IdentifierBindingCase, tmp_path: Path, capsys: pytest.CaptureFixture[str], - monkeypatch: pytest.MonkeyPatch, ) -> None: - original: Callable[..., Any] = project.get_schema_validations - - def without_deferred_requests(**kwargs: Any) -> Any: - assert kwargs["requests"] == () - return original(**kwargs) - - monkeypatch.setattr(project, "get_schema_validations", without_deferred_requests) (tmp_path / "sqlbuild_project.toml").write_text( f'name = "orders"\nadapter = "{test_case.dialect}"\n[rules]\nselect = []\n' ) From 88a1de2b6369c7092b9c8d00aad91c6bd055c245 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:31:51 +0100 Subject: [PATCH 13/36] feat(native)!: complete semantic diagnostics natively for every dialect and text Ports the semantic completion stage's remaining deferrals: Python's \w, \d, \s, re.IGNORECASE and $ semantics, splitlines boundaries, code-point offsets, casefold and upper beyond ASCII. A missing dialect raises the wheel's TypeError and an unknown one its ValueError, as Python's completion did; an internal failure (a panic, an unreadable tree, a location outside the authored lines, a project without its analysis catalog) raises NativeCompilerError. Deletes Python's recover_output_types, explain_diagnostics, recover_diagnostics, opt-out rejection and metadata checks. The native tests now match outputs recorded from those implementations, including non-ASCII projects and the dialects the full parser build added. --- .../_helpers/explanation/columns.rs | 43 +- .../_helpers/explanation/completion.rs | 42 +- .../_helpers/explanation/explain.rs | 78 ++-- .../_helpers/explanation/messages.rs | 116 ++++-- .../_helpers/explanation/opt_outs.rs | 4 +- .../metadata_checks/argument_types.rs | 17 +- .../_helpers/metadata_checks/checks.rs | 42 +- .../_helpers/metadata_checks/families.rs | 12 +- .../metadata_checks/function_calls.rs | 27 +- .../_helpers/metadata_checks/positions.rs | 21 +- .../_helpers/recovery/type_recovery.rs | 11 +- .../semantic_checks/_helpers/sql_text/mod.rs | 1 + .../_helpers/sql_text/parsed_sql.rs | 22 +- .../_helpers/sql_text/python_regex.rs | 78 ++++ .../semantic_checks/_helpers/sql_text/text.rs | 125 +++--- .../src/semantic_checks/constants.rs | 35 +- .../main/check_semantic_metadata.rs | 19 +- .../main/complete_semantic_diagnostics.rs | 17 +- .../main/plan_type_recovery.rs | 19 +- .../src/semantic_checks/models.rs | 79 ++-- .../src/semantic_checks/tests/helpers.rs | 49 ++- .../src/semantic_checks/tests/test_columns.rs | 12 +- .../semantic_checks/tests/test_completion.rs | 40 +- .../semantic_checks/tests/test_messages.rs | 27 +- .../semantic_checks/tests/test_metadata.rs | 17 +- .../semantic_checks/tests/test_parsed_sql.rs | 14 +- .../semantic_checks/tests/test_positions.rs | 14 +- .../tests/test_type_recovery.rs | 2 +- .../src/semantic_checks/tests/test_types.rs | 18 +- crates/sqlbuild-core/src/text/models.rs | 10 + .../_helpers/semantic_checks/completion.rs | 21 +- .../_helpers/semantic_checks/failures.rs | 35 ++ .../_helpers/semantic_checks/metadata.rs | 49 +-- .../bindings/_helpers/semantic_checks/mod.rs | 1 + .../_helpers/semantic_checks/type_recovery.rs | 30 +- scripts/compiler_differential/constants.py | 3 - src/sqlbuild/_native.pyi | 6 +- .../_helpers/assembly/metadata_validation.py | 391 +----------------- .../compile/_helpers/diagnostics/details.py | 296 +------------ .../compile/_helpers/diagnostics/help.py | 20 - .../compile/_helpers/diagnostics/recovery.py | 138 +------ .../diagnostics/sql_analysis_opt_outs.py | 46 --- .../_helpers/diagnostics/type_recovery.py | 299 -------------- .../classes/semantic_completion_inputs.py | 14 - .../semantic_checks/_helpers/completion.py | 13 +- .../semantic_checks/_helpers/deferrals.py | 23 -- .../semantic_checks/_helpers/metadata.py | 13 +- .../semantic_checks/_helpers/stage.py | 25 +- .../semantic_checks/_helpers/type_recovery.py | 16 +- .../compiler/semantic_checks/constants.py | 11 +- .../_complete_native_semantic_diagnostics.py | 4 +- .../compile/semantic_checks/_test_types.py | 1 - .../main/compile/semantic_checks/helpers.py | 41 +- .../test_native_semantic_checks_cli.py | 4 - ...and_comments_new_and_unknown_dialects.json | 42 ++ ...ferences_cursors_audits_and_sql_tests.json | 74 ++++ ...ects_added_to_the_native_parser_build.json | 24 ++ ...nown_dialect_raise_the_wheel_s_errors.json | 16 + ...ii_names_comments_and_line_separators.json | 38 ++ ..._comparisons_type_errors_and_opt_outs.json | 74 ++++ ..._with_pivots_stars_and_set_operations.json | 18 + ...ntic_session_failing_semantics_corpus.json | 26 ++ .../src/sqlbuild/compiler/golden_views.py | 17 +- .../compiler/semantic_checks/_test_types.py | 29 +- .../compiler/semantic_checks/helpers.py | 115 +++--- .../test_native_metadata_checks.py | 65 ++- .../test_native_semantic_checks.py | 200 ++++++--- 67 files changed, 1322 insertions(+), 1927 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/failures.rs delete mode 100644 src/sqlbuild/compiler/compile/_helpers/diagnostics/type_recovery.py delete mode 100644 src/sqlbuild/compiler/semantic_checks/_helpers/deferrals.py create mode 100644 tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json create mode 100644 tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json create mode 100644 tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json create mode 100644 tests/goldens/python_oracle/semantic_formerly_deferred_no_dialect_and_an_unknown_dialect_raise_the_wheel_s_errors.json create mode 100644 tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json create mode 100644 tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json create mode 100644 tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json create mode 100644 tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/columns.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/columns.rs index 8a242d2165..7c5278a784 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/columns.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/columns.rs @@ -2,14 +2,14 @@ use std::cmp::Ordering; -use crate::semantic_checks::_helpers::sql_text::text::{ascii, casefold}; +use crate::semantic_checks::_helpers::sql_text::text::casefold; use crate::semantic_checks::constants::{MAX_EDIT_DISTANCE, MIN_ABBREVIATION_LENGTH}; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; /// Python's `column_distance`: Levenshtein distance between case-folded names. pub(crate) fn column_distance(left: &str, right: &str) -> usize { - let left: Vec = casefold(left).into_bytes(); - let right: Vec = casefold(right).into_bytes(); + let left: Vec = casefold(left).chars().collect(); + let right: Vec = casefold(right).chars().collect(); let mut row: Vec = (0..=right.len()).collect(); for (i, a) in left.iter().enumerate() { let mut following: Vec = Vec::with_capacity(right.len() + 1); @@ -31,12 +31,11 @@ pub(crate) fn column_distance(left: &str, right: &str) -> usize { pub(crate) fn ordered_columns<'a>( name: &str, columns: &'a [(String, String)], -) -> Result, SemanticDeferral> { - ascii(name)?; +) -> Result, SemanticFailure> { + let name_length: usize = name.chars().count(); let mut keyed: Vec<(f64, &'a str)> = Vec::with_capacity(columns.len()); for (column, _) in columns { - ascii(column)?; - let longest = name.len().max(column.len()).max(1); + let longest = name_length.max(column.chars().count()).max(1); keyed.push(( column_distance(name, column) as f64 / longest as f64, column.as_str(), @@ -55,15 +54,17 @@ pub(crate) fn ordered_columns<'a>( pub(crate) fn closest_column<'a>( name: &str, columns: &'a [(String, String)], -) -> Result, SemanticDeferral> { - let folded_name: String = casefold(name); +) -> Result, SemanticFailure> { + let folded_name: Vec = casefold(name).chars().collect(); + let name_first: String = folded_end(name, str::chars); + let name_last: String = folded_end(name, |text| text.chars().rev()); for candidate in ordered_columns(name, columns)? { let distance = column_distance(name, candidate); - let folded_candidate: String = casefold(candidate); - let abbreviation = name.len() >= MIN_ABBREVIATION_LENGTH - && folded_candidate.as_bytes().first() == folded_name.as_bytes().first() - && folded_candidate.as_bytes().last() == folded_name.as_bytes().last() - && is_subsequence(folded_name.as_bytes(), folded_candidate.as_bytes()); + let folded_candidate: Vec = casefold(candidate).chars().collect(); + let abbreviation = name.chars().count() >= MIN_ABBREVIATION_LENGTH + && folded_end(candidate, str::chars) == name_first + && folded_end(candidate, |text| text.chars().rev()) == name_last + && is_subsequence(&folded_name, &folded_candidate); if distance <= MAX_EDIT_DISTANCE || abbreviation { return Ok(Some(candidate)); } @@ -71,8 +72,18 @@ pub(crate) fn closest_column<'a>( Ok(None) } +/// Python's `text[:1].casefold()` or `text[-1:].casefold()`, by the end `characters` reads first. +fn folded_end<'t, I: Iterator>( + text: &'t str, + characters: impl Fn(&'t str) -> I, +) -> String { + characters(text) + .next() + .map_or_else(String::new, |character| casefold(&character.to_string())) +} + /// Python's `all(character in remaining for character in name)` over one shared iterator. -fn is_subsequence(name: &[u8], candidate: &[u8]) -> bool { +fn is_subsequence(name: &[char], candidate: &[char]) -> bool { let mut position: usize = 0; for character in name { match candidate[position..] diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/completion.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/completion.rs index 2c08a9399c..5183917553 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/completion.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/completion.rs @@ -19,25 +19,22 @@ use crate::semantic_checks::_helpers::recovery::poison::Poison; use crate::semantic_checks::_helpers::sql_text::parsed_sql::{ ParsedModelFacts, parsed_model_facts, }; -use crate::semantic_checks::_helpers::sql_text::text::{LineIndex, ascii, plain_text}; +use crate::semantic_checks::_helpers::sql_text::text::LineIndex; use crate::semantic_checks::constants::{ MODEL_RESOURCE_TYPE, SEMANTIC_CODE_PREFIX, SQL_TEST_COLUMN_CODE, SQL_TEST_COLUMN_PATTERN, UNKNOWN_COLUMN_CODE, }; use crate::semantic_checks::models::{ CompletedDiagnostic, CompletionModel, CompletionOutcome, CompletionRequest, FinalDiagnostic, - LineageOutput, SemanticDeferral, SemanticDiagnostic, + LineageOutput, SemanticDiagnostic, SemanticFailure, }; static SQL_TEST_COLUMN: LazyLock> = LazyLock::new(|| compiled(SQL_TEST_COLUMN_PATTERN)); -/// Complete recovery, explanation and opt-out rejection, or defer all three to Python. -pub(crate) fn complete(request: &CompletionRequest) -> CompletionOutcome { - match completed(request) { - Ok(outcome) => outcome, - Err(deferral) => CompletionOutcome::Deferred(deferral), - } +/// Complete recovery, explanation and opt-out rejection. +pub(crate) fn complete(request: &CompletionRequest) -> Result { + completed(request) } /// The request with its shapes and models indexed, and the parsed facts of failing models. @@ -49,7 +46,7 @@ struct Completion<'a> { } impl<'a> Completion<'a> { - fn new(request: &'a CompletionRequest) -> Result { + fn new(request: &'a CompletionRequest) -> Result { let models: HashMap<&'a str, usize> = request .models .iter() @@ -95,14 +92,14 @@ impl<'a> Completion<'a> { .map(|index| (*index, &self.request.models[*index])) } - fn facts(&self, index: usize) -> Result<&ParsedModelFacts, SemanticDeferral> { + fn facts(&self, index: usize) -> Result<&ParsedModelFacts, SemanticFailure> { self.parsed .get(&index) - .ok_or(SemanticDeferral::NativeFailure) + .ok_or_else(|| SemanticFailure::internal("a failing model without parsed facts")) } } -fn completed(request: &CompletionRequest) -> Result { +fn completed(request: &CompletionRequest) -> Result { let completion = Completion::new(request)?; let initial: Vec = request .diagnostics @@ -142,7 +139,7 @@ fn completed(request: &CompletionRequest) -> Result b fn recovered_output( completion: &Completion<'_>, diagnostic: &CompletedDiagnostic, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let Some((index, model)) = completion.model(diagnostic) else { return Ok(None); }; @@ -237,7 +234,7 @@ fn recovered_output( fn recover( completion: &Completion<'_>, diagnostics: Vec, -) -> Result>, SemanticDeferral> { +) -> Result>, SemanticFailure> { let mut poisoned = Poison::default(); let mut origins: HashMap = HashMap::new(); let mut has_roots = false; @@ -300,10 +297,7 @@ fn recover( } /// The `(table, column)` use a diagnostic reports, which a poisoned output may explain. -fn recovery_target( - code: &str, - message: &str, -) -> Result, SemanticDeferral> { +fn recovery_target(code: &str, message: &str) -> Result, SemanticFailure> { let mut target: Option<(String, String)> = None; if code == UNKNOWN_COLUMN_CODE && let Some((column, Some(table))) = missing_column(message)? @@ -311,7 +305,7 @@ fn recovery_target( target = Some((table, column)); } if code == SQL_TEST_COLUMN_CODE - && let Some(captures) = pattern(&SQL_TEST_COLUMN)?.captures(ascii(message)?) + && let Some(captures) = pattern(&SQL_TEST_COLUMN)?.captures(message) { target = Some((captures[1].to_owned(), captures[2].to_owned())); } @@ -322,14 +316,14 @@ fn recovery_target( fn explain( completion: &Completion<'_>, diagnostics: Vec, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let mut binaries: HashMap> = HashMap::new(); let mut explained: Vec = Vec::with_capacity(diagnostics.len()); for diagnostic in diagnostics { let code: &str = &completion.source(&diagnostic).code; match completion.model(&diagnostic) { Some((index, model)) if is_explained_code(code) => { - let authored_sql: &str = plain_text(&model.authored_sql)?; + let authored_sql: &str = &model.authored_sql; if is_type_code(code) && !binaries.contains_key(&index) { binaries.insert(index, binary_index(authored_sql)?); } @@ -360,7 +354,7 @@ fn explain( fn with_semantic_help( diagnostic: CompletedDiagnostic, code: &str, -) -> Result { +) -> Result { let Some(help) = semantic_help(code) else { return Ok(diagnostic); }; @@ -394,7 +388,7 @@ fn hidden_by_opt_out(source: &SemanticDiagnostic, rejected: &BTreeMap<&str, usiz fn reject_opt_outs( completion: &Completion<'_>, diagnostics: &[CompletedDiagnostic], -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let mut rejected: BTreeMap<&str, usize> = BTreeMap::new(); for (index, model) in completion.request.models.iter().enumerate() { if model.rejected_opt_out_file.is_some() { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/explain.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/explain.rs index f6d3eaa26d..e69f3ffa2a 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/explain.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/explain.rs @@ -7,31 +7,48 @@ use regex::Regex; use crate::semantic_checks::_helpers::explanation::columns::{closest_column, ordered_columns}; use crate::semantic_checks::_helpers::explanation::messages::{ - comparison_help, compiled, missing_column, pattern, semantic_help, sentence_message, type_words, + comparison_help, missing_column, pattern, semantic_help, sentence_message, type_words, }; +use crate::semantic_checks::_helpers::sql_text::python_regex::{ignorecase_word, python_regex}; use crate::semantic_checks::_helpers::sql_text::text::{ - LineIndex, ascii, last_newline_before, newlines_before, prefix, + LineIndex, byte_offset, code_point_offset, last_newline_before, newlines_before, prefix, upper, }; use crate::semantic_checks::constants::{ COMPARISON_CODE, DECIMAL_OPERAND_PATTERN, DISPLAY_LIMIT, INTEGER_OPERAND_PATTERN, OPERAND_PATTERN, QUALIFIER_PATTERN, TEMPORAL_OPERAND_PATTERN, TYPE_CODES, UNKNOWN_COLUMN_CODE, }; -use crate::semantic_checks::models::{CompletedDiagnostic, SemanticDeferral, SemanticLocation}; +use crate::semantic_checks::models::{CompletedDiagnostic, SemanticFailure, SemanticLocation}; -static QUALIFIER: LazyLock> = LazyLock::new(|| compiled(QUALIFIER_PATTERN)); +/// A diagnostic located past the model's authored lines, where Python raised `IndexError`. +const OUTSIDE_AUTHORED_LINES: &str = "a diagnostic line outside the model's authored SQL"; + +static QUALIFIER: LazyLock> = + LazyLock::new(|| python_regex(QUALIFIER_PATTERN)); static BINARY: LazyLock> = LazyLock::new(|| { - compiled(&format!( - r"(?i)(?P{OPERAND_PATTERN})\s*(?:>=|<=|<>|!=|=|>|<|\+|\*|/|-)\s*(?P{OPERAND_PATTERN})" + let operand: String = operand_template(); + python_regex(&format!( + r"(?P{operand}){{S}}*(?:>=|<=|<>|!=|=|>|<|\+|\*|/|-){{S}}*(?P{operand})" )) }); static OPERAND_AT: LazyLock> = - LazyLock::new(|| compiled(&format!(r"(?i)\A{OPERAND_PATTERN}"))); + LazyLock::new(|| python_regex(&format!(r"\A{}", operand_template()))); static TEMPORAL_OPERAND: LazyLock> = - LazyLock::new(|| compiled(TEMPORAL_OPERAND_PATTERN)); + LazyLock::new(|| python_regex(&temporal_words(TEMPORAL_OPERAND_PATTERN))); static INTEGER_OPERAND: LazyLock> = - LazyLock::new(|| compiled(INTEGER_OPERAND_PATTERN)); + LazyLock::new(|| python_regex(INTEGER_OPERAND_PATTERN)); static DECIMAL_OPERAND: LazyLock> = - LazyLock::new(|| compiled(DECIMAL_OPERAND_PATTERN)); + LazyLock::new(|| python_regex(DECIMAL_OPERAND_PATTERN)); + +/// `TIMESTAMP` and `DATE` in a template, as Python's `re.IGNORECASE` matches them. +fn temporal_words(template: &str) -> String { + template + .replace("{TIMESTAMP}", &ignorecase_word("TIMESTAMP")) + .replace("{DATE}", &ignorecase_word("DATE")) +} + +fn operand_template() -> String { + temporal_words(OPERAND_PATTERN) +} /// Closed relation shapes by name, each in Python's dict order. pub(crate) type Shapes<'a> = HashMap<&'a str, &'a [(String, String)]>; @@ -50,13 +67,16 @@ pub(crate) struct Binary { } /// Python's `_binary_index`: every binary operation in authored SQL, in order. -pub(crate) fn binary_index(sql: &str) -> Result, SemanticDeferral> { +pub(crate) fn binary_index(sql: &str) -> Result, SemanticFailure> { let binary: &Regex = pattern(&BINARY)?; let mut found: Vec = Vec::new(); for captures in binary.captures_iter(sql) { - let (start, end) = captures - .get(0) - .map_or((0, 0), |item| (item.start(), item.end())); + let (start, end) = captures.get(0).map_or((0, 0), |item| { + ( + code_point_offset(sql, item.start()), + code_point_offset(sql, item.end()), + ) + }); found.push(Binary { start, end, @@ -87,7 +107,7 @@ pub(crate) struct ExplainContext<'a> { pub(crate) fn explain_model( diagnostic: &CompletedDiagnostic, context: &ExplainContext<'_>, -) -> Result { +) -> Result { let ExplainContext { code, model, @@ -203,8 +223,8 @@ fn position(offset: usize) -> i64 { i64::try_from(offset).unwrap_or(i64::MAX) } -/// Python's `lines[line - 1]`, including its negative indexing; out of range is not reproduced. -fn python_line<'a>(lines: &'a LineIndex<'a>, line: i64) -> Result<&'a str, SemanticDeferral> { +/// Python's `lines[line - 1]`, including its negative indexing; out of range is internal. +fn python_line<'a>(lines: &'a LineIndex<'a>, line: i64) -> Result<&'a str, SemanticFailure> { let count: i64 = i64::try_from(lines.lines.len()).unwrap_or(i64::MAX); let index: i64 = if line - 1 < 0 { line - 1 + count @@ -216,13 +236,13 @@ fn python_line<'a>(lines: &'a LineIndex<'a>, line: i64) -> Result<&'a str, Seman .lines .get(index) .copied() - .ok_or(SemanticDeferral::UnexpectedPosition), - Err(_) => Err(SemanticDeferral::UnexpectedPosition), + .ok_or_else(|| SemanticFailure::internal(OUTSIDE_AUTHORED_LINES)), + Err(_) => Err(SemanticFailure::internal(OUTSIDE_AUTHORED_LINES)), } } /// The alias qualifying the column that ends `prefix`, with doubled quotes undone. -fn qualifier(prefix: &str) -> Result, SemanticDeferral> { +fn qualifier(prefix: &str) -> Result, SemanticFailure> { let Some(captures) = pattern(&QUALIFIER)?.captures(prefix) else { return Ok(None); }; @@ -242,12 +262,14 @@ fn binary_at(binaries: &[Binary], offset: i64) -> Option<&Binary> { } /// Python's `re.compile(_OPERAND, re.IGNORECASE).match(sql, offset)`. -fn operand_at(sql: &str, offset: i64) -> Result, SemanticDeferral> { +fn operand_at(sql: &str, offset: i64) -> Result, SemanticFailure> { let operand: &Regex = pattern(&OPERAND_AT)?; let start: usize = usize::try_from(offset.max(0)).unwrap_or(usize::MAX); - Ok(sql - .get(start..) - .and_then(|rest| operand.find(rest)) + if start > sql.chars().count() { + return Ok(None); + } + Ok(operand + .find(&sql[byte_offset(sql, start)..]) .map(|found| found.as_str())) } @@ -256,9 +278,9 @@ fn operand_type( text: &str, aliases: &HashMap, shapes: &Shapes<'_>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { if let Some(captures) = pattern(&TEMPORAL_OPERAND)?.captures(text) { - return Ok(Some(captures[1].to_ascii_uppercase())); + return Ok(Some(upper(&captures[1]))); } if pattern(&INTEGER_OPERAND)?.is_match(text) { return Ok(Some("INTEGER".to_owned())); @@ -273,7 +295,7 @@ fn operand_type( let table: &str = aliases.get(alias).map_or(alias, String::as_str); let value = shape_type(shapes, table, column); return match value { - Some(value) if !value.is_empty() => Ok(Some(ascii(value)?.to_ascii_uppercase())), + Some(value) if !value.is_empty() => Ok(Some(upper(value))), _ => Ok(None), }; } @@ -281,7 +303,7 @@ fn operand_type( let mut candidates: HashSet = HashSet::new(); for table in tables { if let Some(value) = shape_type(shapes, table, text) { - candidates.insert(ascii(value)?.to_ascii_uppercase()); + candidates.insert(upper(value)); } } Ok(if candidates.len() == 1 { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs index 7cb0732827..cf7e9a55e4 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs @@ -2,19 +2,21 @@ use std::sync::LazyLock; -use regex::{Captures, Regex}; +use regex::Regex; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_word::is_python_word; -use crate::semantic_checks::_helpers::sql_text::text::ascii; +use crate::semantic_checks::_helpers::sql_text::python_regex::ignorecase_equal; +use crate::semantic_checks::_helpers::sql_text::text::upper; use crate::semantic_checks::constants::{ BIGQUERY_DIALECT, COMPARISON_CODE, CONTEXT_SUFFIX_PATTERN, DATE_TYPE, MISSING_PATTERN, - QUOTED_PIECE_PATTERN, SEMANTIC_HELP, TIMESTAMP_TYPE, TYPE_WORDS_PATTERN, + QUOTED_PIECE_PATTERN, SEMANTIC_HELP, TIMESTAMP_TYPE, TYPE_WORDS, }; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; -static TYPE_WORDS: LazyLock> = LazyLock::new(|| compiled(TYPE_WORDS_PATTERN)); static MISSING: LazyLock> = LazyLock::new(|| compiled(MISSING_PATTERN)); static CONTEXT_SUFFIX: LazyLock> = - LazyLock::new(|| compiled(CONTEXT_SUFFIX_PATTERN)); + LazyLock::new(|| compiled(&format!(r"{CONTEXT_SUFFIX_PATTERN}\z"))); static QUOTED_PIECE: LazyLock> = LazyLock::new(|| compiled(QUOTED_PIECE_PATTERN)); @@ -23,21 +25,13 @@ pub(crate) fn compiled(pattern: &str) -> Result { Regex::new(pattern).map_err(|error| error.to_string()) } -/// A compiled constant pattern, or a native-failure deferral if it did not compile. +/// A compiled constant pattern, or an internal failure if it did not compile. pub(crate) fn pattern( compiled: &'static Result, -) -> Result<&'static Regex, SemanticDeferral> { +) -> Result<&'static Regex, SemanticFailure> { compiled .as_ref() - .map_err(|_| SemanticDeferral::NativeFailure) -} - -/// A message Python's regexes read the same way, or a deferral. -fn message_text(message: &str) -> Result<&str, SemanticDeferral> { - if message.contains('\n') { - return Err(SemanticDeferral::NonAsciiText); - } - ascii(message) + .map_err(|_| SemanticFailure::internal("a constant pattern does not compile")) } /// Python's `semantic_help`. @@ -48,47 +42,97 @@ pub(crate) fn semantic_help(code: &str) -> Option<&'static str> { .map(|(_, help)| *help) } -/// Python's `sentence_message`: drop the context suffix and upper-case unquoted type words. -pub(crate) fn sentence_message(message: &str) -> Result { +/// Python's `re.sub(r" \(context: [^)]*\)$", "", message)`: Python's `$` also matches before a +/// final newline, which the suffix then keeps. +fn without_context_suffix(message: &str) -> Result { let suffix: &Regex = pattern(&CONTEXT_SUFFIX)?; + let (body, newline) = match message.strip_suffix('\n') { + Some(body) => (body, "\n"), + None => (message, ""), + }; + Ok(format!("{}{newline}", suffix.replace(body, ""))) +} + +/// Python's `sentence_message`: drop the context suffix and upper-case unquoted type words. +pub(crate) fn sentence_message(message: &str) -> Result { let quoted: &Regex = pattern("ED_PIECE)?; - let words: &Regex = pattern(&TYPE_WORDS)?; - let text = suffix.replace(message_text(message)?, ""); + let text: String = without_context_suffix(message)?; let mut sentence = String::with_capacity(text.len()); let mut last: usize = 0; for piece in quoted.find_iter(&text) { - sentence.push_str(&upper_type_words(words, &text[last..piece.start()])); + sentence.push_str(&upper_type_words(&text[last..piece.start()])); sentence.push_str(piece.as_str()); last = piece.end(); } - sentence.push_str(&upper_type_words(words, &text[last..])); + sentence.push_str(&upper_type_words(&text[last..])); Ok(sentence) } -fn upper_type_words(words: &Regex, text: &str) -> String { - words.replace_all(text, upper_match).into_owned() +fn upper_type_words(text: &str) -> String { + let characters: Vec = text.chars().collect(); + let mut result = String::with_capacity(text.len()); + let mut last: usize = 0; + for (start, end) in type_word_spans(&characters) { + result.extend(&characters[last..start]); + result.push_str(&upper(&characters[start..end].iter().collect::())); + last = end; + } + result.extend(&characters[last..]); + result } -fn upper_match(captures: &Captures<'_>) -> String { - captures[0].to_ascii_uppercase() +/// Python's `re.finditer(_TYPE_WORDS, text, re.IGNORECASE)` spans, in code points. +fn type_word_spans(characters: &[char]) -> Vec<(usize, usize)> { + let python = active_python_text(); + let word = |index: usize| { + characters + .get(index) + .is_some_and(|character| is_python_word(python, *character)) + }; + let boundary = |index: usize| (index > 0 && word(index - 1)) != word(index); + let mut spans: Vec<(usize, usize)> = Vec::new(); + let mut start: usize = 0; + while start < characters.len() { + let found: Option = boundary(start) + .then(|| { + TYPE_WORDS.iter().find_map(|candidate| { + let end: usize = start + candidate.len(); + let matches: bool = characters.get(start..end).is_some_and(|slice| { + slice + .iter() + .zip(candidate.chars()) + .all(|(character, letter)| ignorecase_equal(*character, letter)) + }); + (matches && boundary(end)).then_some(end) + }) + }) + .flatten(); + match found { + Some(end) => { + spans.push((start, end)); + start = end; + } + None => start += 1, + } + } + spans } /// Python's `re.findall(_TYPE_WORDS, message, re.IGNORECASE)` upper-cased. -pub(crate) fn type_words(message: &str) -> Result, SemanticDeferral> { - let words: &Regex = pattern(&TYPE_WORDS)?; - let mut found: Vec = Vec::new(); - for captures in words.captures_iter(message_text(message)?) { - found.push(captures[1].to_ascii_uppercase()); - } - Ok(found) +pub(crate) fn type_words(message: &str) -> Result, SemanticFailure> { + let characters: Vec = message.chars().collect(); + Ok(type_word_spans(&characters) + .into_iter() + .map(|(start, end)| upper(&characters[start..end].iter().collect::())) + .collect()) } /// Python's `missing_column`: the unknown column and its table, if the message names one. pub(crate) fn missing_column( message: &str, -) -> Result)>, SemanticDeferral> { +) -> Result)>, SemanticFailure> { let missing: &Regex = pattern(&MISSING)?; - let Some(captures) = missing.captures(message_text(message)?) else { + let Some(captures) = missing.captures(message) else { return Ok(None); }; let table: Option = captures.get(2).map(|table| table.as_str().to_owned()); diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/opt_outs.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/opt_outs.rs index 23a2ad7384..fef4cdbd5d 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/opt_outs.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/opt_outs.rs @@ -9,7 +9,7 @@ use crate::semantic_checks::constants::{ ADDITIONAL_HELP_SEPARATOR, FINDING_KINDS, PROJECT_CONFIG_FILENAME, REQUIRE_SQL_ANALYSIS_KEY, SETTING_SNIPPET_INDENT, SETTINGS_SECTION, }; -use crate::semantic_checks::models::{OptOutDiagnostic, SemanticDeferral}; +use crate::semantic_checks::models::{OptOutDiagnostic, SemanticFailure}; static FINDING_PATTERNS: LazyLock> = LazyLock::new(|| { let alternatives: Vec = FINDING_KINDS @@ -25,7 +25,7 @@ pub(crate) fn opt_out_diagnostic( name: &str, location_file: Option<&str>, hidden_codes: &[&str], -) -> Result { +) -> Result { let findings: String = finding_counts(pattern(&FINDING_PATTERNS)?, hidden_codes); let opt_out: &str = if location_file == Some(PROJECT_CONFIG_FILENAME) { "`sql_analysis = false` from this [path_defaults] entry" diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/argument_types.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/argument_types.rs index affd4eb433..deec9ab0f8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/argument_types.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/argument_types.rs @@ -5,23 +5,23 @@ use std::collections::HashMap; use polyglot_sql::{DataType, Expression}; use serde_json::Value; -use crate::semantic_checks::_helpers::sql_text::text::ascii; +use crate::semantic_checks::_helpers::sql_text::text::upper; use crate::semantic_checks::constants::{ BOOLEAN_RESULT_KINDS, BOOLEAN_TYPE_NAME, COLUMN_KIND, CUSTOM_TYPE_NAME, DECIMAL_TYPE_NAME, KNOWN_CAST_TYPE_NAMES, TIMESTAMP_KIND, TIMESTAMP_WITH_TIME_ZONE_TYPE_NAME, VARCHAR_DATA_TYPES, }; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; /// The type Python infers for `expression`, or None. pub(crate) fn expression_type( expression: &Expression, return_types: &HashMap, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let kind: &str = expression.variant_name(); let name: &str = expression.get_name(); let function_name: &str = if name.is_empty() { kind } else { name }; if kind != COLUMN_KIND - && let Some(declared) = return_types.get(&ascii(function_name)?.to_ascii_uppercase()) + && let Some(declared) = return_types.get(&upper(function_name)) { return Ok(Some(declared.clone())); } @@ -35,9 +35,9 @@ pub(crate) fn expression_type( } /// Python's reading of a cast's serialised `to` payload. -fn cast_type(data_type: &DataType) -> Result, SemanticDeferral> { - let target: Value = - serde_json::to_value(data_type).map_err(|_| SemanticDeferral::NativeFailure)?; +fn cast_type(data_type: &DataType) -> Result, SemanticFailure> { + let target: Value = serde_json::to_value(data_type) + .map_err(|_| SemanticFailure::internal("a cast type does not serialize"))?; let Some(raw_type) = target .get("data_type") .and_then(Value::as_str) @@ -45,7 +45,6 @@ fn cast_type(data_type: &DataType) -> Result, SemanticDeferral> { else { return Ok(None); }; - let raw_type: &str = ascii(raw_type)?; let custom_name: Option<&str> = target .get("name") .and_then(Value::as_str) @@ -53,7 +52,7 @@ fn cast_type(data_type: &DataType) -> Result, SemanticDeferral> { if raw_type.eq_ignore_ascii_case(CUSTOM_TYPE_NAME) && let Some(name) = custom_name { - return Ok(Some(ascii(name)?.to_ascii_uppercase())); + return Ok(Some(upper(name))); } let lowered: String = raw_type.to_ascii_lowercase(); if lowered == TIMESTAMP_KIND && target.get("timezone") == Some(&Value::Bool(true)) { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/checks.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/checks.rs index b299cb02b6..759ba69c99 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/checks.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/checks.rs @@ -13,14 +13,14 @@ use crate::semantic_checks::_helpers::metadata_checks::function_calls::{ use crate::semantic_checks::_helpers::metadata_checks::positions::{ find_code_point, text_position, }; -use crate::semantic_checks::_helpers::sql_text::text::{ascii, casefold}; +use crate::semantic_checks::_helpers::sql_text::text::casefold; use crate::semantic_checks::constants::{ CURSOR_INPUTS_KEY, SQL_TEST_COLUMN_CODE, SQL_TEST_CTE_PATTERN, TYPE_MISMATCH_CODE, UNKNOWN_COLUMN_REFERENCE_CODE, UNKNOWN_TYPE, }; use crate::semantic_checks::models::{ MetadataFinding, MetadataFunction, MetadataModel, MetadataOutcome, MetadataRequest, - MetadataSource, MetadataSqlTest, ModelMetadataFindings, SemanticDeferral, + MetadataSource, MetadataSqlTest, ModelMetadataFindings, SemanticFailure, }; use crate::type_system::models::TypeFamily; @@ -38,10 +38,10 @@ type Shapes<'a> = HashMap<&'a str, &'a [(String, String)]>; /// One config reference: `(key, column name, the shape it must name a column of)`. type Reference<'a> = (String, &'a str, &'a [(String, String)]); -/// The metadata errors of every model, source and SQL test, or a deferral. +/// The metadata errors of every model, source and SQL test, or the error Python raised. pub(crate) fn check_metadata( request: &MetadataRequest, -) -> Result { +) -> Result { let shapes: Shapes<'_> = request .shapes .iter() @@ -78,7 +78,7 @@ pub(crate) fn check_metadata( fn model_errors( model: &MetadataModel, context: &FunctionContext<'_>, -) -> Result { +) -> Result { if !model.checked { return Ok(ModelMetadataFindings::default()); } @@ -102,7 +102,7 @@ fn model_error( code: &'static str, name: &str, message: String, -) -> Result { +) -> Result { let (line, column) = text_position(&model.authored_sql, name, 0)?; Ok(MetadataFinding { code, @@ -113,10 +113,10 @@ fn model_error( } /// Python's `{column.casefold() for column in shape}`. -fn folded_names(shape: &[(String, String)]) -> Result, SemanticDeferral> { +fn folded_names(shape: &[(String, String)]) -> Result, SemanticFailure> { let mut names: HashSet = HashSet::with_capacity(shape.len()); for (name, _) in shape { - names.insert(casefold(ascii(name)?)); + names.insert(casefold(name)); } Ok(names) } @@ -126,7 +126,7 @@ fn reference_errors( model: &MetadataModel, shape: &[(String, String)], context: &FunctionContext<'_>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let mut references: Vec> = Vec::new(); for (key, names) in &model.references { for name in names { @@ -146,7 +146,7 @@ fn reference_errors( } let mut errors: Vec = Vec::new(); for (key, name, reference_shape) in references { - if !folded_names(reference_shape)?.contains(&casefold(ascii(name)?)) { + if !folded_names(reference_shape)?.contains(&casefold(name)) { let message = format!("{key} references unknown column '{name}'"); errors.push(model_error( model, @@ -167,20 +167,20 @@ fn cursor_error( model: &MetadataModel, shape: &[(String, String)], families: &Families, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let (Some(cursor), Some(cursor_type)) = (model.cursor.as_deref(), model.cursor_type.as_deref()) else { return Ok(None); }; - let folded_cursor: String = casefold(ascii(cursor)?); + let folded_cursor: String = casefold(cursor); let mut actual: &str = UNKNOWN_TYPE; for (name, column_type) in shape { - if casefold(ascii(name)?) == folded_cursor { + if casefold(name) == folded_cursor { actual = column_type; break; } } - let allowed: &[TypeFamily] = match ascii(cursor_type)?.to_ascii_lowercase().as_str() { + let allowed: &[TypeFamily] = match cursor_type.to_ascii_lowercase().as_str() { "integer" => INTEGER_CURSOR_FAMILIES, "timestamp" | "date" => TEMPORAL_CURSOR_FAMILIES, _ => return Ok(None), @@ -203,14 +203,14 @@ fn cursor_error( fn source_errors( sources: &[MetadataSource], shapes: &Shapes<'_>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let mut errors: Vec<(usize, MetadataFinding)> = Vec::new(); for (index, source) in sources.iter().enumerate() { let (Some(cursor), Some(shape)) = (&source.cursor_column, shapes.get(source.name.as_str())) else { continue; }; - if folded_names(shape)?.contains(&casefold(ascii(cursor)?)) { + if folded_names(shape)?.contains(&casefold(cursor)) { continue; } let (line, column) = text_position(&source.contents, cursor, 0)?; @@ -231,23 +231,21 @@ fn source_errors( fn sql_test_errors( tests: &[MetadataSqlTest], shapes: &Shapes<'_>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let cte_pattern: &Regex = pattern(&SQL_TEST_CTE)?; let mut errors: Vec<(usize, MetadataFinding, i64)> = Vec::new(); for (index, test) in tests.iter().enumerate() { for (cte_name, columns) in &test.ctes { - if cte_name.ends_with('\n') { - return Err(SemanticDeferral::NonAsciiText); - } + let matched_name: &str = cte_name.strip_suffix('\n').unwrap_or(cte_name); let Some(shape) = cte_pattern - .captures(cte_name) + .captures(matched_name) .and_then(|captures| shapes.get(&captures[1])) else { continue; }; let available: HashSet = folded_names(shape)?; for column_name in columns { - if available.contains(&casefold(ascii(column_name)?)) { + if available.contains(&casefold(column_name)) { continue; } let offset: usize = find_code_point(&test.contents, cte_name).unwrap_or(0); diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs index b005765755..e993a1bad8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/families.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use crate::semantic_checks::constants::GENERIC_DIALECT; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; use crate::type_system::main::normalize_type::normalize_type; use crate::type_system::models::{TypeFamily, TypeNormalization}; @@ -29,10 +29,10 @@ impl Families { } } - /// The family of `type_sql`, or a deferral where Python would leave the native type system. - pub(crate) fn family(&self, type_sql: &str) -> Result { - let normalization: TypeNormalization = normalize_type(type_sql, &self.dialect) - .map_err(|_| SemanticDeferral::UnsupportedType)?; + /// The family of `type_sql`, or the error Python's type normalization raised for it. + pub(crate) fn family(&self, type_sql: &str) -> Result { + let normalization: TypeNormalization = + normalize_type(type_sql, &self.dialect).map_err(SemanticFailure::TypeNormalization)?; if normalization.parse_error.is_some() { self.fallback_types.borrow_mut().push(type_sql.to_owned()); } @@ -40,7 +40,7 @@ impl Families { } /// Python's `_different_argument_families`. - pub(crate) fn different(&self, left: &str, right: &str) -> Result { + pub(crate) fn different(&self, left: &str, right: &str) -> Result { let left: TypeFamily = self.family(left)?; let right: TypeFamily = self.family(right)?; let families: [TypeFamily; 2] = [left, right]; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/function_calls.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/function_calls.rs index 1c9bc2ec0b..d892147d2d 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/function_calls.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/function_calls.rs @@ -8,12 +8,12 @@ use polyglot_sql::{Expression, ExpressionWalk}; use crate::semantic_checks::_helpers::metadata_checks::argument_types::expression_type; use crate::semantic_checks::_helpers::metadata_checks::families::Families; use crate::semantic_checks::_helpers::sql_text::parsed_sql::parsed_model; -use crate::semantic_checks::_helpers::sql_text::text::{ascii, casefold}; +use crate::semantic_checks::_helpers::sql_text::text::casefold; use crate::semantic_checks::constants::{ ARGUMENT_COUNT_CODE, NUMBER_LITERAL_TYPE, STRING_LITERAL_TYPE, TYPE_MISMATCH_CODE, UDF_NAME_PREFIX, UNKNOWN_TYPE, }; -use crate::semantic_checks::models::{MetadataFunction, MetadataModel, SemanticDeferral}; +use crate::semantic_checks::models::{MetadataFunction, MetadataModel, SemanticFailure}; /// One function error before it is located: `(code, located name, message)`. pub(crate) type FunctionError = (&'static str, String, String); @@ -41,7 +41,7 @@ type FoldedShape = HashMap>; pub(crate) fn function_errors( model: &MetadataModel, context: &FunctionContext<'_>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { if context.functions.is_empty() || !model.calls_functions { return Ok(Vec::new()); } @@ -101,7 +101,7 @@ fn call_name(call: &Function) -> &str { fn declared_calls<'a>( root: &'a Expression, functions: &HashMap<&str, &'a MetadataFunction>, -) -> Result<(Vec>, Vec<&'a Select>), SemanticDeferral> { +) -> Result<(Vec>, Vec<&'a Select>), SemanticFailure> { let mut calls: Vec> = Vec::new(); let mut selects: Vec<&'a Select> = Vec::new(); let mut pending: Vec<(&'a Expression, Option)> = vec![(root, None)]; @@ -112,7 +112,7 @@ fn declared_calls<'a>( selects.push(select); } Expression::Function(call) => { - let key: String = casefold(ascii(call_name(call))?); + let key: String = casefold(call_name(call)); if let Some(function) = functions.get(key.as_str()) { calls.push(DeclaredCall { call, @@ -169,7 +169,7 @@ fn relation_names(relation: &Expression) -> (&str, &str) { fn folded_shapes<'a>( relations: &HashMap>, shapes: &HashMap<&str, &[(String, String)]>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let mut folded: HashMap<&'a str, FoldedShape> = HashMap::new(); for relation in relations.values().flatten() { let (table_name, _) = relation_names(relation); @@ -182,7 +182,7 @@ fn folded_shapes<'a>( let mut columns: FoldedShape = HashMap::new(); for (name, column_type) in *shape { columns - .entry(casefold(ascii(name)?)) + .entry(casefold(name)) .or_default() .push(column_type.clone()); } @@ -197,7 +197,7 @@ fn argument_type_of( scope: Option<&[&Expression]>, folded: &HashMap<&str, FoldedShape>, context: &FunctionContext<'_>, -) -> Result { +) -> Result { match argument { Expression::Literal(literal) => Ok(match literal.as_ref() { Literal::String(_) => STRING_LITERAL_TYPE, @@ -216,20 +216,17 @@ fn argument_column_type( column: &Column, scope: Option<&[&Expression]>, folded: &HashMap<&str, FoldedShape>, -) -> Result { +) -> Result { let Some(relations) = scope else { return Ok(UNKNOWN_TYPE.to_owned()); }; - let qualifier: Option = match &column.table { - Some(table) => Some(casefold(ascii(&table.name)?)), - None => None, - }; - let name: String = casefold(ascii(&column.name.name)?); + let qualifier: Option = column.table.as_ref().map(|table| casefold(&table.name)); + let name: String = casefold(&column.name.name); let mut candidates: Vec<&String> = Vec::new(); for relation in relations { let (table_name, alias) = relation_names(relation); if let Some(qualifier) = &qualifier - && *qualifier != casefold(ascii(alias)?) + && *qualifier != casefold(alias) { continue; } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/positions.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/positions.rs index a18abde30d..2c1b593632 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/positions.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/metadata_checks/positions.rs @@ -1,14 +1,13 @@ //! Python's `_text_position`: the first whole-word occurrence of a name, in code points. -use crate::semantic_checks::models::SemanticDeferral; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_word::is_python_word; -/// Python's `\w` for a character next to a match; beyond ASCII the two engines may disagree. -fn word_character(character: char) -> Result { - if character.is_ascii() { - Ok(character.is_ascii_alphanumeric() || character == '_') - } else { - Err(SemanticDeferral::NonAsciiText) - } +use crate::semantic_checks::models::SemanticFailure; + +/// Python's `\w` for a character next to a match. +fn word_character(character: char) -> Result { + Ok(is_python_word(active_python_text(), character)) } /// Python's `text.find(needle)` in code points, or None. @@ -22,7 +21,7 @@ fn whole_word_at( name: &[char], start: usize, position: usize, -) -> Result { +) -> Result { if characters[position..position + name.len()] != *name { return Ok(false); } @@ -40,7 +39,7 @@ fn match_start( characters: &[char], name: &[char], offset: usize, -) -> Result { +) -> Result { let start: usize = offset.min(characters.len()); if name.len() <= characters.len() - start { for position in start..=characters.len() - name.len() { @@ -57,7 +56,7 @@ pub(crate) fn text_position( text: &str, name: &str, offset: usize, -) -> Result<(i64, i64), SemanticDeferral> { +) -> Result<(i64, i64), SemanticFailure> { let characters: Vec = text.chars().collect(); let name: Vec = name.chars().collect(); let start: usize = match_start(&characters, &name, offset)?; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/recovery/type_recovery.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/recovery/type_recovery.rs index 225ef721ee..1e065476f8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/recovery/type_recovery.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/recovery/type_recovery.rs @@ -6,20 +6,17 @@ use crate::semantic_checks::_helpers::recovery::bindings::{BindingOwner, binding use crate::semantic_checks::_helpers::recovery::poison::Poison; use crate::semantic_checks::_helpers::sql_text::parsed_sql::{normalized_sql, projection_spans}; use crate::semantic_checks::models::{ - RecoveryModel, SemanticDeferral, TypeRecoveryOutcome, TypeRecoveryPlan, TypeRecoveryRequest, + RecoveryModel, SemanticFailure, TypeRecoveryOutcome, TypeRecoveryPlan, TypeRecoveryRequest, TypeRecoveryStep, }; use crate::semantic_checks::types::RevisedBinding; /// Find poisoned outputs and the failed models Python must revalidate with unknown types. -pub(crate) fn plan(request: &TypeRecoveryRequest) -> TypeRecoveryStep { - match planned(request) { - Ok(step) => step, - Err(deferral) => TypeRecoveryStep::Deferred(deferral), - } +pub(crate) fn plan(request: &TypeRecoveryRequest) -> Result { + planned(request) } -fn planned(request: &TypeRecoveryRequest) -> Result { +fn planned(request: &TypeRecoveryRequest) -> Result { let failed: Vec = failed_models(request); if !has_blocking_error(request, &failed) { return Ok(TypeRecoveryStep::Unchanged); diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/mod.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/mod.rs index 4eeca97b60..0f26611802 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/mod.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod parsed_sql; +pub(crate) mod python_regex; pub(crate) mod text; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs index 63240fc79b..ca3f08dca7 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/parsed_sql.rs @@ -11,42 +11,42 @@ use crate::semantic_checks::constants::{ CLOSE_PARENTHESIS, GENERIC_DIALECT, MAX_FUNCTION_CALL_DEPTH, OPEN_PARENTHESIS, PROJECTION_SEPARATOR, }; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; use crate::semantic_validation::main::normalize::normalize_analysis_sql; use crate::semantic_validation::models::NormalizationInput; /// The Polyglot dialect the wheel resolves `name` to, when Polyglot knows the name. -pub(crate) fn polyglot_dialect(name: &str) -> Result { +pub(crate) fn polyglot_dialect(name: &str) -> Result { name.parse::() .map(Dialect::get) - .map_err(|_| SemanticDeferral::UnsupportedDialect) + .map_err(|_| SemanticFailure::UnknownDialect(name.to_owned())) } /// Python's `get_complete_schema_binding_request(...).sql` without placeholders. pub(crate) fn normalized_sql( query_sql: &str, dialect: Option<&str>, -) -> Result { +) -> Result { normalize_analysis_sql(NormalizationInput { sql: query_sql.to_owned(), dialect: dialect.unwrap_or(GENERIC_DIALECT).to_owned(), stubs: HashMap::new(), placeholders: HashMap::new(), }) - .map_err(|_| SemanticDeferral::UnreadableSql) + .map_err(|_| SemanticFailure::internal("model SQL does not normalize")) } /// Python's `_projection_spans`: top-level SELECT-list item spans in code points. pub(crate) fn projection_spans( sql: &str, dialect: Option<&str>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let Some(name) = dialect else { - return Err(SemanticDeferral::UnsupportedDialect); + return Err(SemanticFailure::NoDialect); }; let tokens = polyglot_dialect(name)? .tokenize(sql) - .map_err(|_| SemanticDeferral::UnreadableSql)?; + .map_err(|_| SemanticFailure::internal("model SQL does not tokenize"))?; let mut depth: i64 = 0; let mut start: Option = None; let mut spans: Vec<(usize, usize)> = Vec::new(); @@ -103,7 +103,7 @@ pub(crate) struct ParsedModelFacts { pub(crate) fn parsed_model_facts( query_sql: &str, dialect: Option<&str>, -) -> Result { +) -> Result { let Some(parsed) = parsed_model(query_sql, dialect)? else { return Ok(ParsedModelFacts::default()); }; @@ -117,13 +117,13 @@ pub(crate) fn parsed_model_facts( pub(crate) fn parsed_model( query_sql: &str, dialect: Option<&str>, -) -> Result, SemanticDeferral> { +) -> Result, SemanticFailure> { let sql = normalized_sql(query_sql, dialect)?; let parser = polyglot_dialect(dialect.unwrap_or(GENERIC_DIALECT))?; let guard: ComplexityGuardOptions = serde_json::from_value(serde_json::json!({ "maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH, })) - .map_err(|_| SemanticDeferral::NativeFailure)?; + .map_err(|_| SemanticFailure::internal("the parse guard options do not build"))?; let options = ParseOptions { complexity_guard: Some(guard), }; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs new file mode 100644 index 0000000000..4dba074976 --- /dev/null +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs @@ -0,0 +1,78 @@ +//! Regexes that match as Python's `re` does for this lane's patterns. +//! +//! Python's `\w`, `\d` and `\s` follow `str.isalnum()`, `str.isdecimal()` and `str.isspace()` of +//! the running Python, and `re.IGNORECASE` also matches `ı`, `İ`, `ſ` and the Kelvin sign to ASCII +//! letters; the `regex` crate differs on each beyond ASCII. Patterns spell those classes out. + +use std::fmt::Write; +use std::sync::LazyLock; + +use regex::Regex; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::models::PythonText; + +/// `(template token, class)`: Python's `\w`, `\d`, `\s` and an ignore-case identifier start. +static CLASSES: LazyLock<[(&'static str, String); 4]> = LazyLock::new(|| { + let python: PythonText = active_python_text(); + [ + ("{W}", ranges_class("_", python.alnum_ranges())), + ("{D}", ranges_class("", python.decimal_ranges())), + ( + "{S}", + String::from( + r"[\t-\r\x{1c}-\x{20}\x{85}\x{a0}\x{1680}\x{2000}-\x{200a}\x{2028}\x{2029}\x{202f}\x{205f}\x{3000}]", + ), + ), + ( + "{L}", + String::from(r"[A-Za-z_\x{130}\x{131}\x{17f}\x{212a}]"), + ), + ] +}); + +fn ranges_class(extra: &str, ranges: &[(u32, u32)]) -> String { + let mut class: String = format!("[{extra}"); + for (start, end) in ranges { + let _ = write!(class, r"\x{{{start:x}}}-\x{{{end:x}}}"); + } + class.push(']'); + class +} + +/// The characters `re.IGNORECASE` matches to one ASCII letter, as a class. +fn ignorecase_letter(letter: char) -> String { + let extra: &str = match letter.to_ascii_lowercase() { + 'i' => r"\x{130}\x{131}", + 'k' => r"\x{212a}", + 's' => r"\x{17f}", + _ => "", + }; + format!( + "[{}{}{extra}]", + letter.to_ascii_lowercase(), + letter.to_ascii_uppercase() + ) +} + +/// Python's `re.IGNORECASE` match of an ASCII word, as a sequence of classes. +pub(crate) fn ignorecase_word(word: &str) -> String { + word.chars().map(ignorecase_letter).collect() +} + +/// Whether `character` matches the ASCII letter `letter` under Python's `re.IGNORECASE`. +pub(crate) fn ignorecase_equal(character: char, letter: char) -> bool { + character.eq_ignore_ascii_case(&letter) + || matches!( + (letter.to_ascii_lowercase(), character), + ('i', '\u{130}' | '\u{131}') | ('k', '\u{212a}') | ('s', '\u{17f}') + ) +} + +/// Compile a pattern template, replacing `{W}`, `{D}`, `{S}` and `{L}` with Python's classes. +pub(crate) fn python_regex(template: &str) -> Result { + let mut pattern: String = template.to_owned(); + for (token, class) in CLASSES.iter() { + pattern = pattern.replace(token, class); + } + Regex::new(&pattern).map_err(|error| error.to_string()) +} diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/text.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/text.rs index 9537c2ac40..220b519cff 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/text.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/text.rs @@ -1,54 +1,56 @@ -//! Python string operations over ASCII text, where byte offsets equal code-point offsets. +//! Python string operations, with offsets in code points as Python's string indexes count them. -use crate::semantic_checks::models::SemanticDeferral; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::main::python_upper::python_upper; -/// ASCII text or a deferral, because Python's case folding and regex classes differ beyond it. -pub(crate) fn ascii(text: &str) -> Result<&str, SemanticDeferral> { - if text.is_ascii() { - Ok(text) - } else { - Err(SemanticDeferral::NonAsciiText) - } +/// Python's `str.casefold`. +pub(crate) fn casefold(text: &str) -> String { + python_casefold(active_python_text(), text) } -/// ASCII text without the separators Python's `\s` matches and Rust's does not. -pub(crate) fn plain_text(text: &str) -> Result<&str, SemanticDeferral> { - if ascii(text)? - .bytes() - .any(|byte| (0x1c..=0x1f).contains(&byte)) - { - return Err(SemanticDeferral::NonAsciiText); - } - Ok(text) +/// Python's `str.upper`. +pub(crate) fn upper(text: &str) -> String { + python_upper(active_python_text(), text) } -/// Python's `str.casefold` on ASCII text. -pub(crate) fn casefold(text: &str) -> String { - text.to_ascii_lowercase() +/// The line boundaries Python's `str.splitlines` splits on. +fn is_line_boundary(character: char) -> bool { + matches!( + character, + '\n' | '\r' + | '\u{b}' + | '\u{c}' + | '\u{1c}' + | '\u{1d}' + | '\u{1e}' + | '\u{85}' + | '\u{2028}' + | '\u{2029}' + ) } -/// Python's `str.splitlines(keepends=True)` line lengths on ASCII text. -fn line_ends(text: &str) -> Vec<(usize, usize)> { - let bytes = text.as_bytes(); - let mut lines: Vec<(usize, usize)> = Vec::new(); +/// Python's `str.splitlines(keepends=True)` as `(start, content end, next start)` byte offsets. +fn line_ends(text: &str) -> Vec<(usize, usize, usize)> { + let mut lines: Vec<(usize, usize, usize)> = Vec::new(); let mut start: usize = 0; - let mut index: usize = 0; - while index < bytes.len() { - let byte = bytes[index]; - if matches!(byte, b'\n' | b'\r' | 0x0b | 0x0c | 0x1c | 0x1d | 0x1e) { - let content_end = index; - index += 1; - if byte == b'\r' && bytes.get(index) == Some(&b'\n') { - index += 1; - } - lines.push((start, content_end)); - start = index; - } else { - index += 1; + let mut characters = text.char_indices().peekable(); + while let Some((index, character)) = characters.next() { + if !is_line_boundary(character) { + continue; } + let mut next: usize = index + character.len_utf8(); + if character == '\r' + && let Some((_, '\n')) = characters.peek() + { + let _ = characters.next(); + next += 1; + } + lines.push((start, index, next)); + start = next; } - if start < bytes.len() { - lines.push((start, bytes.len())); + if start < text.len() { + lines.push((start, text.len(), text.len())); } lines } @@ -56,6 +58,7 @@ fn line_ends(text: &str) -> Vec<(usize, usize)> { /// Python's `_line_index`: `splitlines()` and the running starts of `splitlines(keepends=True)`. pub(crate) struct LineIndex<'a> { pub(crate) lines: Vec<&'a str>, + /// Code-point offsets of each line start, then the text length. pub(crate) starts: Vec, } @@ -64,11 +67,10 @@ impl<'a> LineIndex<'a> { let ends = line_ends(text); let mut starts: Vec = vec![0]; let mut lines: Vec<&'a str> = Vec::with_capacity(ends.len()); - for (index, (start, content_end)) in ends.iter().enumerate() { - lines.push(&text[*start..*content_end]); - let next_start = ends.get(index + 1).map_or(text.len(), |(next, _)| *next); + for (start, content_end, next_start) in ends { + lines.push(&text[start..content_end]); let previous = starts.last().copied().unwrap_or(0); - starts.push(previous + (next_start - start)); + starts.push(previous + text[start..next_start].chars().count()); } Self { lines, starts } } @@ -85,9 +87,9 @@ impl<'a> LineIndex<'a> { pub(crate) fn newlines_before(text: &str, end: i64) -> i64 { let end: usize = clamp(text, end); i64::try_from( - text.as_bytes()[..end] - .iter() - .filter(|byte| **byte == b'\n') + text.chars() + .take(end) + .filter(|character| *character == '\n') .count(), ) .unwrap_or(i64::MAX) @@ -96,19 +98,34 @@ pub(crate) fn newlines_before(text: &str, end: i64) -> i64 { /// Python's `text.rfind("\n", 0, end)` with Python's slice clamping. pub(crate) fn last_newline_before(text: &str, end: i64) -> i64 { let end: usize = clamp(text, end); - text.as_bytes()[..end] - .iter() - .rposition(|byte| *byte == b'\n') - .map_or(-1, |index| i64::try_from(index).unwrap_or(i64::MAX)) + text.chars() + .take(end) + .enumerate() + .filter(|(_, character)| *character == '\n') + .last() + .map_or(-1, |(index, _)| i64::try_from(index).unwrap_or(i64::MAX)) } +/// A Python slice bound over `text`'s code points: negative counts from the end. fn clamp(text: &str, end: i64) -> usize { - let length: i64 = i64::try_from(text.len()).unwrap_or(i64::MAX); + let length: i64 = i64::try_from(text.chars().count()).unwrap_or(i64::MAX); let end: i64 = if end < 0 { (length + end).max(0) } else { end }; usize::try_from(end.min(length)).unwrap_or(0) } -/// Python's `text[:end]` for a non-negative end on ASCII text. +/// Python's `text[:end]`. pub(crate) fn prefix(text: &str, end: i64) -> &str { - &text[..clamp(text, end)] + &text[..byte_offset(text, clamp(text, end))] +} + +/// The byte offset of code point `index`, or the text length past the end. +pub(crate) fn byte_offset(text: &str, index: usize) -> usize { + text.char_indices() + .nth(index) + .map_or(text.len(), |(byte, _)| byte) +} + +/// The code-point offset of byte offset `byte` on a character boundary. +pub(crate) fn code_point_offset(text: &str, byte: usize) -> usize { + text[..byte].chars().count() } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/constants.rs b/crates/sqlbuild-analysis/src/semantic_checks/constants.rs index d423aae445..7c9a4a7cc2 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/constants.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/constants.rs @@ -77,15 +77,36 @@ pub(crate) const KNOWN_CAST_TYPE_NAMES: [(&str, &str); 13] = [ ("varchar", "TEXT"), ]; -pub(crate) const TYPE_WORDS_PATTERN: &str = r"(?i)\b(timestamp|integer|varchar|boolean|date|interval|double|float|decimal|numeric|bigint|smallint|text|time|string|binary|array|struct)\b"; +/// Python's `_TYPE_WORDS` alternatives, tried in order between word boundaries. +pub(crate) const TYPE_WORDS: [&str; 18] = [ + "timestamp", + "integer", + "varchar", + "boolean", + "date", + "interval", + "double", + "float", + "decimal", + "numeric", + "bigint", + "smallint", + "text", + "time", + "string", + "binary", + "array", + "struct", +]; pub(crate) const MISSING_PATTERN: &str = r"Unknown column '([^']+)'(?: in table '([^']+)')?"; -pub(crate) const CONTEXT_SUFFIX_PATTERN: &str = r" \(context: [^)]*\)$"; +pub(crate) const CONTEXT_SUFFIX_PATTERN: &str = r" \(context: [^)]*\)"; pub(crate) const QUOTED_PIECE_PATTERN: &str = r"'[^']*'"; -pub(crate) const OPERAND_PATTERN: &str = r"(?:TIMESTAMP\s+'[^']*'|DATE\s+'[^']*'|'(?:[^']|'')*'|-?\d+(?:\.\d+)?|[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)?)"; -pub(crate) const QUALIFIER_PATTERN: &str = r#"(?:"((?:[^"]|"")+)"|([A-Za-z_]\w*))\.$"#; -pub(crate) const TEMPORAL_OPERAND_PATTERN: &str = r"(?i)\A(TIMESTAMP|DATE)\s*'"; -pub(crate) const INTEGER_OPERAND_PATTERN: &str = r"\A-?\d+\z"; -pub(crate) const DECIMAL_OPERAND_PATTERN: &str = r"\A-?\d+\.\d+\z"; +/// Python's `_OPERAND` under `re.IGNORECASE`, as a `python_regex` template. +pub(crate) const OPERAND_PATTERN: &str = r"(?:{TIMESTAMP}{S}+'[^']*'|{DATE}{S}+'[^']*'|'(?:[^']|'')*'|-?{D}+(?:\.{D}+)?|{L}{W}*(?:\.{L}{W}*)?)"; +pub(crate) const QUALIFIER_PATTERN: &str = r#"(?:"((?:[^"]|"")+)"|([A-Za-z_]{W}*))\.\z"#; +pub(crate) const TEMPORAL_OPERAND_PATTERN: &str = r"\A({TIMESTAMP}|{DATE}){S}*'"; +pub(crate) const INTEGER_OPERAND_PATTERN: &str = r"\A-?{D}+\z"; +pub(crate) const DECIMAL_OPERAND_PATTERN: &str = r"\A-?{D}+\.{D}+\z"; pub(crate) const SQL_TEST_COLUMN_PATTERN: &str = r"'__(?:expected|ref)__(.+)' names unknown column '([^']+)'"; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs index d49d847409..32e71cf42a 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs @@ -3,17 +3,20 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::semantic_checks::_helpers::metadata_checks::checks::check_metadata; -use crate::semantic_checks::models::{MetadataOutcome, MetadataRequest, SemanticDeferral}; +use crate::semantic_checks::models::{MetadataOutcome, MetadataRequest, SemanticFailure}; use crate::semantic_validation::models::ProjectCatalog; -/// Check metadata on the compile's analysis pool; a deferral hands the checks back to Python. +/// Check metadata on the compile's analysis pool. +/// +/// # Errors +/// +/// The error Python's checks raised for the same input, or an internal native failure. pub fn check_semantic_metadata( request: &MetadataRequest, catalog: &ProjectCatalog, -) -> Result, String> { - let pool = catalog.analysis_pool()?; - Ok(pool.install(|| { - catch_compiler_panic(|| Ok(check_metadata(request))) - .unwrap_or(Err(SemanticDeferral::NativeFailure)) - })) +) -> Result { + let pool = catalog.analysis_pool().map_err(SemanticFailure::Internal)?; + pool.install(|| { + catch_compiler_panic(|| Ok(check_metadata(request))).map_err(SemanticFailure::Internal)? + }) } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs index 1d0f251c6b..dda6fcd18b 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs @@ -3,17 +3,20 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::semantic_checks::_helpers::explanation::completion::complete; -use crate::semantic_checks::models::{CompletionOutcome, CompletionRequest, SemanticDeferral}; +use crate::semantic_checks::models::{CompletionOutcome, CompletionRequest, SemanticFailure}; use crate::semantic_validation::models::ProjectCatalog; /// Python's recovery, explanation and opt-out rejection, on the compile's analysis pool. +/// +/// # Errors +/// +/// The error Python's completion raised for the same input, or an internal native failure. pub fn complete_semantic_diagnostics( request: &CompletionRequest, catalog: &ProjectCatalog, -) -> Result { - let pool = catalog.analysis_pool()?; - Ok(pool.install(|| { - catch_compiler_panic(|| Ok(complete(request))) - .unwrap_or(CompletionOutcome::Deferred(SemanticDeferral::NativeFailure)) - })) +) -> Result { + let pool = catalog.analysis_pool().map_err(SemanticFailure::Internal)?; + pool.install(|| { + catch_compiler_panic(|| Ok(complete(request))).map_err(SemanticFailure::Internal)? + }) } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs index 2cfab0de26..148adf0931 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs @@ -3,17 +3,20 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::semantic_checks::_helpers::recovery::type_recovery::plan; -use crate::semantic_checks::models::{SemanticDeferral, TypeRecoveryRequest, TypeRecoveryStep}; +use crate::semantic_checks::models::{SemanticFailure, TypeRecoveryRequest, TypeRecoveryStep}; use crate::semantic_validation::models::ProjectCatalog; -/// Plan type recovery on the compile's analysis pool; a native panic defers to Python. +/// Plan type recovery on the compile's analysis pool. +/// +/// # Errors +/// +/// The error Python's recovery raised for the same input, or an internal native failure. pub fn plan_type_recovery( request: &TypeRecoveryRequest, catalog: &ProjectCatalog, -) -> Result { - let pool = catalog.analysis_pool()?; - Ok(pool.install(|| { - catch_compiler_panic(|| Ok(plan(request))) - .unwrap_or(TypeRecoveryStep::Deferred(SemanticDeferral::NativeFailure)) - })) +) -> Result { + let pool = catalog.analysis_pool().map_err(SemanticFailure::Internal)?; + pool.install(|| { + catch_compiler_panic(|| Ok(plan(request))).map_err(SemanticFailure::Internal)? + }) } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/models.rs b/crates/sqlbuild-analysis/src/semantic_checks/models.rs index 8aaa052cea..9abbe08a66 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/models.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/models.rs @@ -4,6 +4,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use crate::semantic_checks::types::CompletedParts; +use crate::type_system::models::TypeNormalizationError; /// One compiled model output column and the upstream columns it reads. #[derive(Clone, Debug, Default)] @@ -60,28 +61,24 @@ pub struct TypeRecoveryRequest { pub diagnostics: Vec, } -/// Why native semantic completion hands a stage back to Python. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum SemanticDeferral { - UnsupportedDialect, - UnreadableSql, - NonAsciiText, - UnexpectedPosition, - UnsupportedType, - NativeFailure, +/// Why native semantic completion fails: the error Python's completion raised for the same +/// input, or an internal native failure. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum SemanticFailure { + /// The wheel's `TypeError` for tokenizing without a dialect. + NoDialect, + /// The wheel's `ValueError` for a dialect name Polyglot does not know. + UnknownDialect(String), + /// The error Python's type normalization raised. + TypeNormalization(TypeNormalizationError), + /// An internal native failure, raised as `NativeCompilerError`. + Internal(String), } -impl SemanticDeferral { - /// The deferral kind recorded for the harness. - pub fn as_str(self) -> &'static str { - match self { - Self::UnsupportedDialect => "unsupported_dialect", - Self::UnreadableSql => "unreadable_sql", - Self::NonAsciiText => "non_ascii_text", - Self::UnexpectedPosition => "unexpected_position", - Self::UnsupportedType => "unsupported_type", - Self::NativeFailure => "native_failure", - } +impl SemanticFailure { + /// An internal failure with a fixed reason that never quotes SQL. + pub(crate) fn internal(reason: &str) -> Self { + Self::Internal(reason.to_owned()) } } @@ -109,29 +106,19 @@ impl TypeRecoveryPlan { } } -/// Type recovery's first step: nothing to recover, a plan, or a deferral. +/// Type recovery's first step: nothing to recover, or a plan. #[derive(Clone, Debug)] pub enum TypeRecoveryStep { Unchanged, Planned(TypeRecoveryPlan), - Deferred(SemanticDeferral), } impl TypeRecoveryStep { - /// `unchanged`, `planned` or `deferred`. + /// `unchanged` or `planned`. pub fn status(&self) -> &'static str { match self { Self::Unchanged => "unchanged", Self::Planned(_) => "planned", - Self::Deferred(_) => "deferred", - } - } - - /// The deferral kind, when the stage is handed back to Python. - pub fn deferral(&self) -> Option<&'static str> { - match self { - Self::Deferred(deferral) => Some(deferral.as_str()), - _ => None, } } @@ -240,29 +227,19 @@ impl FinalDiagnostic { } } -/// Recovery, explanation and opt-out rejection, or a deferral. +/// Recovery, explanation and opt-out rejection. #[derive(Clone, Debug)] -pub enum CompletionOutcome { - Completed { - diagnostics: Vec, - /// Indexes into `diagnostics` per model, or `None` when models keep their bindings. - model_bindings: Option>>, - order: Vec, - }, - Deferred(SemanticDeferral), +pub struct CompletionOutcome { + pub diagnostics: Vec, + /// Indexes into `diagnostics` per model, or `None` when models keep their bindings. + pub model_bindings: Option>>, + pub order: Vec, } impl CompletionOutcome { - /// `(deferral kind, completed parts)`: exactly one side is set. - pub fn into_parts(self) -> (Option<&'static str>, Option) { - match self { - Self::Completed { - diagnostics, - model_bindings, - order, - } => (None, Some((diagnostics, model_bindings, order))), - Self::Deferred(deferral) => (Some(deferral.as_str()), None), - } + /// The completed diagnostics, model binding positions and final order. + pub fn into_parts(self) -> CompletedParts { + (self.diagnostics, self.model_bindings, self.order) } } diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs index 3d8ce62526..88faa0d3e6 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs @@ -13,8 +13,8 @@ use crate::semantic_checks::models::{ CompletedDiagnostic, CompletionModel, CompletionRequest, DiagnosticOwner, FinalDiagnostic, LineageOutput, MetadataFinding, MetadataFunction, MetadataModel, MetadataOutcome, MetadataRequest, MetadataSource, MetadataSqlTest, ModelBinding, ModelMetadataFindings, - RawBinding, RecoveryModel, SemanticDeferral, SemanticDiagnostic, SemanticLocation, - TypeRecoveryPlan, TypeRecoveryRequest, + RawBinding, RecoveryModel, SemanticDiagnostic, SemanticFailure, SemanticLocation, + TypeRecoveryPlan, TypeRecoveryRequest, TypeRecoveryStep, }; use crate::semantic_checks::tests::test_types::{ CompletionTestCase, DescribedDiagnostic, MetadataTestCase, OperandTypeTestCase, @@ -24,16 +24,16 @@ use crate::semantic_validation::models::ProjectCatalog; /// What type recovery decided: status, poisoned outputs, revalidated models, kept and bindings. pub(crate) type RecoverySummary = ( - &'static str, + String, Vec<(String, String)>, Vec, Vec<(usize, Option)>, Vec>, ); -/// What completion decided: deferral, final diagnostics and model binding positions. +/// What completion decided: its failure, final diagnostics and model binding positions. pub(crate) type CompletionSummary = ( - Option<&'static str>, + Option, Vec, Option>>, ); @@ -57,21 +57,21 @@ pub(crate) fn names(values: &[&str]) -> BTreeSet { strings(values).into_iter().collect() } -pub(crate) fn sentence(message: &str) -> Result { +pub(crate) fn sentence(message: &str) -> Result { sentence_message(message) } /// The missing column and table Python reads from a message. pub(crate) fn missing_parts( message: &str, -) -> Result)>, SemanticDeferral> { +) -> Result)>, SemanticFailure> { missing_column(message) } /// An expected missing column and table as owned strings. pub(crate) fn owned_missing( - expected: Result)>, SemanticDeferral>, -) -> Result)>, SemanticDeferral> { + expected: Result)>, SemanticFailure>, +) -> Result)>, SemanticFailure> { expected.map(|missing| missing.map(owned_parts)) } @@ -197,7 +197,10 @@ fn poisoned_chain(dialect: Option<&str>, is_error: bool) -> TypeRecoveryRequest /// Plan and finish type recovery for one case of the poisoned chain. pub(crate) fn recovery_summary(test_case: &TypeRecoveryTestCase) -> RecoverySummary { let request = poisoned_chain(test_case.dialect, test_case.errors); - let step = plan_type_recovery(&request, &catalog()).expect("the analysis pool runs"); + let (status, step) = match plan_type_recovery(&request, &catalog()) { + Ok(step) => (step.status().to_owned(), step), + Err(failure) => (format!("{failure:?}"), TypeRecoveryStep::Unchanged), + }; let revised = vec![ test_case .revised @@ -210,7 +213,7 @@ pub(crate) fn recovery_summary(test_case: &TypeRecoveryTestCase) -> RecoverySumm .map(|plan| finish_type_recovery(&request, plan, &revised).expect("finishes")) .unwrap_or_default(); ( - step.deferral().unwrap_or(step.status()), + status, step.plan() .map(TypeRecoveryPlan::poisoned_outputs) .unwrap_or_default(), @@ -405,14 +408,16 @@ pub(crate) fn completion_summary(test_case: &CompletionTestCase) -> CompletionSu request.diagnostics.truncate(kept_diagnostics); request.models[2].rejected_opt_out_file = [Some("legacy.sql".to_owned()), None][usize::from(test_case.without_diagnostics)].clone(); - let outcome = complete_semantic_diagnostics(&request, &catalog()).expect("the pool runs"); - let (deferral, parts) = outcome.into_parts(); - let (diagnostics, model_bindings, order) = parts.unwrap_or_default(); + let (failure, (diagnostics, model_bindings, order)) = + match complete_semantic_diagnostics(&request, &catalog()) { + Ok(outcome) => (None, outcome.into_parts()), + Err(failure) => (Some(failure), Default::default()), + }; let described_order: Vec = order .into_iter() .map(|entry| described_entry(&request, &diagnostics, entry)) .collect(); - (deferral, described_order, model_bindings) + (failure, described_order, model_bindings) } fn described_entry( @@ -493,14 +498,16 @@ pub(crate) fn operand_type_summary(test_case: &OperandTypeTestCase) -> Completio .map(|(name, columns)| ((*name).to_owned(), shape(columns))) .collect(), }; - let outcome = complete_semantic_diagnostics(&request, &catalog()).expect("the pool runs"); - let (deferral, parts) = outcome.into_parts(); - let (diagnostics, model_bindings, order) = parts.unwrap_or_default(); + let (failure, (diagnostics, model_bindings, order)) = + match complete_semantic_diagnostics(&request, &catalog()) { + Ok(outcome) => (None, outcome.into_parts()), + Err(failure) => (Some(failure), Default::default()), + }; let described_order: Vec = order .into_iter() .map(|entry| described_entry(&request, &diagnostics, entry)) .collect(); - (deferral, described_order, model_bindings) + (failure, described_order, model_bindings) } /// The type Python infers for the first projection of `SELECT {expression}` on DuckDB. @@ -616,8 +623,8 @@ pub(crate) fn metadata_finding( /// Run the metadata checks on a fresh catalog. pub(crate) fn metadata_outcome( test_case: &MetadataTestCase, -) -> Result { - check_semantic_metadata(&metadata_request(test_case), &catalog()).expect("the pool runs") +) -> Result { + check_semantic_metadata(&metadata_request(test_case), &catalog()) } /// The findings Python reports for the checked mart of `metadata_request`. diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_columns.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_columns.rs index c1bb607569..c90344ca94 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_columns.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_columns.rs @@ -1,5 +1,4 @@ use crate::semantic_checks::_helpers::explanation::columns::{closest_column, ordered_columns}; -use crate::semantic_checks::models::SemanticDeferral; use crate::semantic_checks::tests::helpers::shape; use crate::semantic_checks::tests::test_types::ClosestColumnTestCase; @@ -45,8 +44,15 @@ fn given_unknown_column_names_when_suggesting_then_matches_python_order_and_choi description: "a column Python case-folds beyond ASCII", name: "strasse", columns: &[("stra\u{df}e", "VARCHAR")], - expected_closest: Err(SemanticDeferral::NonAsciiText), - expected_order: Err(SemanticDeferral::NonAsciiText), + expected_closest: Ok(Some("stra\u{df}e")), + expected_order: Ok(&["stra\u{df}e"]), + }, + ClosestColumnTestCase { + description: "an abbreviation whose first character case-folds to two", + name: "\u{df}ab", + columns: &[("ssxab", "X"), ("sab", "Y")], + expected_closest: Ok(Some("ssxab")), + expected_order: Ok(&["ssxab", "sab"]), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_completion.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_completion.rs index 3d21831efe..83d1ef49a8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_completion.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_completion.rs @@ -16,7 +16,7 @@ fn given_failing_project_when_completing_then_recovers_explains_and_rejects_like description: "a recovered typo, an explained comparison, a test error and an opt-out", non_ascii_comment: false, without_diagnostics: false, - expected_deferral: None, + expected_failure: None, expected_diagnostics: vec![ described( "B002", @@ -47,18 +47,44 @@ fn given_failing_project_when_completing_then_recovers_explains_and_rejects_like expected_bindings: Some(vec![vec![0], vec![1], vec![2]]), }, CompletionTestCase { - description: "non-ASCII authored SQL in an explained model defers to Python", + description: "a trailing non-ASCII comment leaves every explanation unchanged", non_ascii_comment: true, without_diagnostics: false, - expected_deferral: Some("non_ascii_text"), - expected_diagnostics: Vec::new(), - expected_bindings: None, + expected_failure: None, + expected_diagnostics: vec![ + described( + "B002", + "Unknown column 'amonut' in raw_orders", + Some("did you mean 'amount'?"), + &[ + "raw_orders has: amount, status, customer_id, order_id", + "1 downstream uses of stg.amount were not checked because of this error", + ], + Some((5, 18, None, None)), + ), + described( + "B217", + "Cannot compare TIMESTAMP and INTEGER", + Some("compare with a timestamp, for example TIMESTAMP '2026-04-01'"), + &["o.ordered_at is TIMESTAMP, 5 is INTEGER"], + Some((7, 7, Some(7), Some(23))), + ), + described("B002", "Unknown column 'x' in table 'stg'", None, &[], None), + described( + "P009", + "`sql_analysis false` is not needed for model 'legacy'", + Some(OPT_OUT_HELP), + &[OPT_OUT_NOTE], + None, + ), + ], + expected_bindings: Some(vec![vec![0], vec![1], vec![2]]), }, CompletionTestCase { description: "no diagnostics keep every model's bindings", non_ascii_comment: false, without_diagnostics: true, - expected_deferral: None, + expected_failure: None, expected_diagnostics: Vec::new(), expected_bindings: None, }, @@ -67,7 +93,7 @@ fn given_failing_project_when_completing_then_recovers_explains_and_rejects_like assert_eq!( completion_summary(&test_case), ( - test_case.expected_deferral, + test_case.expected_failure.clone(), test_case.expected_diagnostics.clone(), test_case.expected_bindings.clone(), ), diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_messages.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_messages.rs index b3b665e148..c270825d06 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_messages.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_messages.rs @@ -1,5 +1,4 @@ use crate::semantic_checks::_helpers::explanation::messages::comparison_help; -use crate::semantic_checks::models::SemanticDeferral; use crate::semantic_checks::tests::helpers::{missing_parts, owned_missing, sentence}; use crate::semantic_checks::tests::test_types::{ComparisonHelpTestCase, SentenceMessageTestCase}; @@ -31,16 +30,30 @@ fn given_binding_messages_when_rewriting_then_matches_python_sentences() { expected_missing: Ok(Some(("amonut", None))), }, SentenceMessageTestCase { - description: "a non-ASCII column Python's regex classes read differently", + description: "a non-ASCII column", message: "Unknown column 'stra\u{df}e'", - expected_sentence: Err(SemanticDeferral::NonAsciiText), - expected_missing: Err(SemanticDeferral::NonAsciiText), + expected_sentence: Ok("Unknown column 'stra\u{df}e'"), + expected_missing: Ok(Some(("stra\u{df}e", None))), }, SentenceMessageTestCase { - description: "a line break Python's end anchor treats specially", + description: "a line break before the table", message: "Unknown column 'a'\nin table 'b'", - expected_sentence: Err(SemanticDeferral::NonAsciiText), - expected_missing: Err(SemanticDeferral::NonAsciiText), + expected_sentence: Ok("Unknown column 'a'\nin table 'b'"), + expected_missing: Ok(Some(("a", None))), + }, + SentenceMessageTestCase { + description: "type words Python's ignore-case matches beyond ASCII, before a final newline", + message: "Cannot compare \u{130}nteger with \u{131}nterval (context: x)\n", + expected_sentence: Ok("Cannot compare \u{130}NTEGER with INTERVAL\n"), + expected_missing: Ok(None), + }, + SentenceMessageTestCase { + description: "word boundaries next to non-ASCII word characters", + message: "\u{e9}integer integer\u{e9} \u{17f}tring va\u{212a}lue 'integer' DATE", + expected_sentence: Ok( + "\u{e9}integer integer\u{e9} STRING va\u{212a}lue 'integer' DATE", + ), + expected_missing: Ok(None), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_metadata.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_metadata.rs index cf0c72bd2b..a5339d94e8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_metadata.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_metadata.rs @@ -1,4 +1,3 @@ -use crate::semantic_checks::models::SemanticDeferral; use crate::semantic_checks::tests::helpers::{expected_checked_outcome, metadata_outcome}; use crate::semantic_checks::tests::test_types::MetadataTestCase; @@ -9,7 +8,7 @@ const MART_SHAPE: &[(&str, &str)] = &[ ]; #[test] -fn given_project_metadata_when_checking_then_reports_python_errors_or_defers() { +fn given_project_metadata_when_checking_then_reports_python_errors() { let test_cases = [ MetadataTestCase { description: "function arity and families, config references, cursors and SQL tests", @@ -18,16 +17,20 @@ fn given_project_metadata_when_checking_then_reports_python_errors_or_defers() { expected_outcome: Ok(expected_checked_outcome()), }, MetadataTestCase { - description: "a dialect outside the native parser build", + description: "a dialect the all-dialects parser build added", dialect: "mysql", mart_shape: MART_SHAPE, - expected_outcome: Err(SemanticDeferral::UnsupportedDialect), + expected_outcome: Ok(expected_checked_outcome()), }, MetadataTestCase { - description: "a column name Python case-folds beyond ASCII", + description: "a column name Python case-folds beyond ASCII to a tested name", dialect: "duckdb", - mart_shape: &[("stra\u{df}e", "VARCHAR")], - expected_outcome: Err(SemanticDeferral::NonAsciiText), + mart_shape: &[ + ("order_id", "INTEGER"), + ("\u{17f}caled", "DOUBLE"), + ("label", "VARCHAR"), + ], + expected_outcome: Ok(expected_checked_outcome()), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_parsed_sql.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_parsed_sql.rs index fa69b6ac56..ad30d836f9 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_parsed_sql.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_parsed_sql.rs @@ -1,5 +1,5 @@ use crate::semantic_checks::_helpers::sql_text::parsed_sql::projection_spans; -use crate::semantic_checks::models::SemanticDeferral; +use crate::semantic_checks::models::SemanticFailure; use crate::semantic_checks::tests::helpers::{names, pairs, parsed_facts}; use crate::semantic_checks::tests::test_types::{ParsedFactsTestCase, ProjectionSpanTestCase}; @@ -40,13 +40,19 @@ fn given_select_lists_when_finding_projection_spans_then_matches_python_tokens() description: "no dialect, which the wheel's tokenizer rejects", sql: "SELECT a", dialect: None, - expected_spans: Err(SemanticDeferral::UnsupportedDialect), + expected_spans: Err(SemanticFailure::NoDialect), }, ProjectionSpanTestCase { - description: "a dialect outside the native parser build", + description: "a dialect the all-dialects parser build added, as the wheel tokenizes it", sql: "SELECT a", dialect: Some("mysql"), - expected_spans: Err(SemanticDeferral::UnsupportedDialect), + expected_spans: Ok(&[(6, 8)]), + }, + ProjectionSpanTestCase { + description: "a dialect Polyglot does not know", + sql: "SELECT a", + dialect: Some("nonsense"), + expected_spans: Err(SemanticFailure::UnknownDialect("nonsense".to_owned())), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_positions.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_positions.rs index bc56c3c127..8a58643df2 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_positions.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_positions.rs @@ -1,5 +1,4 @@ use crate::semantic_checks::_helpers::metadata_checks::positions::text_position; -use crate::semantic_checks::models::SemanticDeferral; use crate::semantic_checks::tests::test_types::TextPositionTestCase; #[test] @@ -48,11 +47,18 @@ fn given_names_in_text_when_locating_then_matches_python_whole_word_positions() expected_position: Ok((1, 1)), }, TextPositionTestCase { - description: "a non-ASCII neighbour Python's word class may read differently", - text: "\u{e9}amount", + description: "a non-ASCII letter before the name is part of the word", + text: "\u{e9}amount amount", name: "amount", offset: 0, - expected_position: Err(SemanticDeferral::NonAsciiText), + expected_position: Ok((1, 9)), + }, + TextPositionTestCase { + description: "a non-ASCII digit joins the word and a line separator is no newline", + text: "a\u{2028}b \u{663}amount amount", + name: "amount", + offset: 0, + expected_position: Ok((1, 13)), }, ]; for test_case in test_cases { diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_type_recovery.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_type_recovery.rs index e96abd40b0..ed35ef599c 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_type_recovery.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_type_recovery.rs @@ -37,7 +37,7 @@ fn given_poisoned_projection_when_recovering_types_then_counts_and_drops_like_py dialect: None, errors: true, revised: &[], - expected_status: "unsupported_dialect", + expected_status: "NoDialect", expected_poisoned: &[], expected_revalidated: &[], expected_kept: &[], diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_types.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_types.rs index 4645a0cf36..9300442f68 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/test_types.rs @@ -1,4 +1,4 @@ -use crate::semantic_checks::models::{MetadataOutcome, SemanticDeferral}; +use crate::semantic_checks::models::{MetadataOutcome, SemanticFailure}; /// One located `(code, message, help, notes, location)` diagnostic of a completed project. pub(crate) type DescribedDiagnostic = ( @@ -13,7 +13,7 @@ pub(crate) struct ProjectionSpanTestCase { pub(crate) description: &'static str, pub(crate) sql: &'static str, pub(crate) dialect: Option<&'static str>, - pub(crate) expected_spans: Result<&'static [(usize, usize)], SemanticDeferral>, + pub(crate) expected_spans: Result<&'static [(usize, usize)], SemanticFailure>, } pub(crate) struct ParsedFactsTestCase { @@ -27,16 +27,16 @@ pub(crate) struct ClosestColumnTestCase { pub(crate) description: &'static str, pub(crate) name: &'static str, pub(crate) columns: &'static [(&'static str, &'static str)], - pub(crate) expected_closest: Result, SemanticDeferral>, - pub(crate) expected_order: Result<&'static [&'static str], SemanticDeferral>, + pub(crate) expected_closest: Result, SemanticFailure>, + pub(crate) expected_order: Result<&'static [&'static str], SemanticFailure>, } pub(crate) struct SentenceMessageTestCase { pub(crate) description: &'static str, pub(crate) message: &'static str, - pub(crate) expected_sentence: Result<&'static str, SemanticDeferral>, + pub(crate) expected_sentence: Result<&'static str, SemanticFailure>, pub(crate) expected_missing: - Result)>, SemanticDeferral>, + Result)>, SemanticFailure>, } pub(crate) struct ComparisonHelpTestCase { @@ -62,7 +62,7 @@ pub(crate) struct CompletionTestCase { pub(crate) description: &'static str, pub(crate) non_ascii_comment: bool, pub(crate) without_diagnostics: bool, - pub(crate) expected_deferral: Option<&'static str>, + pub(crate) expected_failure: Option, pub(crate) expected_diagnostics: Vec, pub(crate) expected_bindings: Option>>, } @@ -79,7 +79,7 @@ pub(crate) struct TextPositionTestCase { pub(crate) text: &'static str, pub(crate) name: &'static str, pub(crate) offset: usize, - pub(crate) expected_position: Result<(i64, i64), SemanticDeferral>, + pub(crate) expected_position: Result<(i64, i64), SemanticFailure>, } pub(crate) struct ArgumentTypeTestCase { @@ -92,5 +92,5 @@ pub(crate) struct MetadataTestCase { pub(crate) description: &'static str, pub(crate) dialect: &'static str, pub(crate) mart_shape: &'static [(&'static str, &'static str)], - pub(crate) expected_outcome: Result, + pub(crate) expected_outcome: Result, } diff --git a/crates/sqlbuild-core/src/text/models.rs b/crates/sqlbuild-core/src/text/models.rs index 31bdf25aa0..8ffa5f92d4 100644 --- a/crates/sqlbuild-core/src/text/models.rs +++ b/crates/sqlbuild-core/src/text/models.rs @@ -123,6 +123,16 @@ impl PythonText { pub fn minor_version(&self) -> u8 { self.minor_version } + + /// Inclusive code point ranges where `str.isalnum()` is true, in order. + pub fn alnum_ranges(&self) -> &'static [(u32, u32)] { + self.alnum_ranges + } + + /// Inclusive code point ranges where `str.isdecimal()` (regex `\d`) is true, in order. + pub fn decimal_ranges(&self) -> &'static [(u32, u32)] { + self.decimal_ranges + } } /// The characters `inspect.cleandoc` strips from line starts. diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs index c01e4e67d9..c853ed9954 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs @@ -10,6 +10,7 @@ use sqlbuild_analysis::semantic_checks::models::{ use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::semantic_checks::failures::semantic_error; use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; use crate::bindings::_helpers::semantic_checks::type_recovery::{LineageInput, lineage_outputs}; use crate::bindings::models::ProjectCatalog; @@ -60,13 +61,8 @@ type CompletedRow = ( ); /// `(completed position, (model, message, note, help))`: exactly one side is set. type OrderRow = (Option, Option<(usize, String, String, String)>); -/// `(deferral, completed, model binding positions, order)`. -type OutcomeRow = ( - Option<&'static str>, - Vec, - Option>>, - Vec, -); +/// `(completed, model binding positions, order)`. +type OutcomeRow = (Vec, Option>>, Vec); /// Recover, explain and reject opt-outs, reading facts the payload omits from `session`. #[pyfunction] @@ -79,13 +75,12 @@ fn complete_semantic_checks( ) -> PyResult { let request = completion_request(request, session.as_deref()).map_err(compiler_error)?; let catalog = &catalog.inner; - let outcome = py - .compiler_detach(|| complete_semantic_diagnostics(&request, catalog)) - .map_err(compiler_error)?; - let (deferral, parts) = outcome.into_parts(); - let (diagnostics, model_bindings, order) = parts.unwrap_or_default(); + let (diagnostics, model_bindings, order) = py + .compiler_detach(|| Ok(complete_semantic_diagnostics(&request, catalog))) + .map_err(compiler_error)? + .map_err(|failure| semantic_error(py, &failure))? + .into_parts(); Ok(( - deferral, diagnostics.into_iter().map(completed_row).collect(), model_bindings, order.into_iter().map(order_row).collect(), diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/failures.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/failures.rs new file mode 100644 index 0000000000..8535b64c6c --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/failures.rs @@ -0,0 +1,35 @@ +//! The exception a failed native semantic completion step raises. + +use pyo3::exceptions::{PyTypeError, PyValueError}; +use pyo3::{PyErr, Python}; +use sqlbuild_analysis::semantic_checks::models::SemanticFailure; +use sqlbuild_core::constants::PANIC_MESSAGE; +use sqlbuild_core::panics::main::is_compiler_panic::is_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; + +use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::type_system::normalization::normalization_error; + +const SEMANTIC_CONTEXT: &str = "native semantic completion"; +/// The wheel's `TypeError` for `tokenize(sql, dialect=None)`. +const NO_DIALECT_MESSAGE: &str = "argument 'dialect': 'None' is not an instance of 'str'"; + +/// The error Python's semantic completion raised for the same input, or `NativeCompilerError`. +pub(crate) fn semantic_error(python: Python<'_>, failure: &SemanticFailure) -> PyErr { + match failure { + SemanticFailure::NoDialect => PyTypeError::new_err(NO_DIALECT_MESSAGE), + SemanticFailure::UnknownDialect(name) => { + PyValueError::new_err(format!("Unknown dialect: {name}")) + } + SemanticFailure::TypeNormalization(error) => normalization_error(python, error), + SemanticFailure::Internal(reason) => internal_error(reason), + } +} + +/// An internal native failure as `NativeCompilerError`, keeping an already placed context. +pub(crate) fn internal_error(reason: &str) -> PyErr { + if is_compiler_panic(reason) && reason != PANIC_MESSAGE { + return compiler_error(reason); + } + compiler_error(native_failure(SEMANTIC_CONTEXT, reason)) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/metadata.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/metadata.rs index b53ae01f31..26edf624ae 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/metadata.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/metadata.rs @@ -11,6 +11,7 @@ use sqlbuild_analysis::semantic_checks::models::{ use std::sync::Arc; use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::semantic_checks::failures::semantic_error; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -41,9 +42,8 @@ type RequestInput = ( ); /// `(code, message, line, column)`. type ErrorRow = (&'static str, String, i64, i64); -/// `(deferral, [(function, reference findings)], source, SQL test findings, fallback types)`. +/// `([(function, reference findings)], source, SQL test findings, fallback types)`. type OutcomeRow = ( - Option<&'static str>, Vec<(Vec, Vec)>, Vec<(usize, ErrorRow)>, Vec<(usize, ErrorRow, i64)>, @@ -59,33 +59,24 @@ fn check_semantic_metadata_rows( ) -> PyResult { let request = metadata_request(request)?; let catalog = &catalog.inner; - let checked = py - .compiler_detach(|| check_semantic_metadata(&request, catalog)) - .map_err(compiler_error)?; - Ok(match checked { - Err(deferral) => ( - Some(deferral.as_str()), - Vec::new(), - Vec::new(), - Vec::new(), - Vec::new(), - ), - Ok(outcome) => ( - None, - outcome.models.into_iter().map(model_rows).collect(), - outcome - .sources - .into_iter() - .map(|(index, error)| (index, error_row(error))) - .collect(), - outcome - .sql_tests - .into_iter() - .map(|(index, error, end)| (index, error_row(error), end)) - .collect(), - outcome.fallback_types, - ), - }) + let outcome = py + .compiler_detach(|| Ok(check_semantic_metadata(&request, catalog))) + .map_err(compiler_error)? + .map_err(|failure| semantic_error(py, &failure))?; + Ok(( + outcome.models.into_iter().map(model_rows).collect(), + outcome + .sources + .into_iter() + .map(|(index, error)| (index, error_row(error))) + .collect(), + outcome + .sql_tests + .into_iter() + .map(|(index, error, end)| (index, error_row(error), end)) + .collect(), + outcome.fallback_types, + )) } fn error_row(error: MetadataFinding) -> ErrorRow { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs index 1565c82749..b02c83a208 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod completion; +pub(crate) mod failures; pub(crate) mod metadata; pub(crate) mod registration; pub(crate) mod session_facts; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index 801971e2a4..82882184d4 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -12,6 +12,7 @@ use sqlbuild_analysis::semantic_checks::types::RevisedBinding; use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::semantic_checks::failures::{internal_error, semantic_error}; use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -46,18 +47,12 @@ pub(crate) struct SemanticTypeRecovery { #[pymethods] impl SemanticTypeRecovery { - /// `unchanged`, `planned` or `deferred`. + /// `unchanged` or `planned`. #[getter] fn status(&self) -> &'static str { self.step.status() } - /// The deferral kind, when the stage is handed back to Python. - #[getter] - fn deferral(&self) -> Option<&'static str> { - self.step.deferral() - } - /// Poisoned `(model, column)` outputs in Python's dict order. #[getter] fn poisoned(&self) -> Vec<(String, String)> { @@ -76,13 +71,15 @@ impl SemanticTypeRecovery { .unwrap_or_default() } - /// Retained diagnostics and model binding positions, or None to defer to Python. - fn finish(&self, revised: Vec>) -> Option { - let plan = self.step.plan()?; - match finish_type_recovery(&self.request, plan, &revised) { - Ok(outcome) => Some((outcome.kept, outcome.model_bindings)), - Err(_) => None, - } + /// Retained diagnostics and model binding positions; an internal failure raises. + fn finish(&self, revised: Vec>) -> PyResult { + let plan = self + .step + .plan() + .ok_or_else(|| internal_error("finishing a type recovery that has no plan"))?; + finish_type_recovery(&self.request, plan, &revised) + .map(|outcome| (outcome.kept, outcome.model_bindings)) + .map_err(|reason| internal_error(&reason)) } } @@ -98,8 +95,9 @@ fn plan_semantic_type_recovery( let request = recovery_request(request, session.as_deref()).map_err(compiler_error)?; let catalog = &catalog.inner; let step = py - .compiler_detach(|| plan_type_recovery(&request, catalog)) - .map_err(compiler_error)?; + .compiler_detach(|| Ok(plan_type_recovery(&request, catalog))) + .map_err(compiler_error)? + .map_err(|failure| semantic_error(py, &failure))?; Ok(SemanticTypeRecovery { request, step }) } diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index d8ad31da50..17420f0efd 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -26,9 +26,6 @@ NATIVE_FALLBACK_PROJECT_CORPUS: str = "project" NATIVE_FALLBACK_DEFERRAL_STAGES: dict[str, str] = { "analysis_session": "model_analysis", - "type_recovery.py": "semantic_checks", - "recovery.py": "semantic_checks", - "metadata_validation.py": "semantic_checks", "contracts/columns.py": "contracts", } NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE: str = "analysis" diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 5e90fd808b..60602133b9 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -464,14 +464,12 @@ class SemanticTypeRecovery: @property def status(self) -> str: ... @property - def deferral(self) -> str | None: ... - @property def poisoned(self) -> list[tuple[str, str]]: ... @property def revalidated(self) -> list[int]: ... def finish( self, revised: list[list[tuple[str, int | None, int | None]]], / - ) -> tuple[list[tuple[int, str | None]], list[list[int]]] | None: ... + ) -> tuple[list[tuple[int, str | None]], list[list[int]]]: ... def plan_semantic_type_recovery( catalog: object, @@ -519,7 +517,6 @@ def check_semantic_metadata_rows( ], /, ) -> tuple[ - str | None, list[tuple[list[tuple[str, str, int, int]], list[tuple[str, str, int, int]]]], list[tuple[int, tuple[str, str, int, int]]], list[tuple[int, tuple[str, str, int, int], int]], @@ -559,7 +556,6 @@ def complete_semantic_checks( session: NativeModelAnalysisSession | None = None, /, ) -> tuple[ - str | None, list[ tuple[ int, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py index 00b756a42c..4de42529d1 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py @@ -3,27 +3,18 @@ from __future__ import annotations import re -from collections.abc import Iterator from functools import lru_cache -from typing import Any, cast from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.adapter.contract.types import TypeFamily -from sqlbuild.adapter.type_system.main.normalize_type import normalize_type -from sqlbuild.compiler.compile._helpers.analysis.columns import _polyglot_expression_type from sqlbuild.compiler.compile._helpers.analysis.compact import ( analyze_columns_and_lineage_with_polyglot, analyze_queries_with_compact_polyglot_batch, - get_complete_schema_binding_request, ) from sqlbuild.compiler.compile._helpers.assembly.native_declarations import ( known_declared_types, known_function_names, ) -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes -from sqlbuild.compiler.compile._helpers.diagnostics.resource_sql import get_resource_sql_diagnostics from sqlbuild.compiler.compile.models import ( - CompiledFunction, CompiledModel, CompiledModelSqlTestPayload, CompiledProject, @@ -36,152 +27,15 @@ DiagnosticPhase, DiagnosticSeverity, ) -from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.constants import BINDING_UNKNOWN_TABLE_INTERNAL_CODE from sqlbuild.compiler.sql_analysis.main._resolve_binding_column import resolve_binding_column from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql from sqlbuild.compiler.sql_analysis.models import SqlBindingResult, SqlSchemaValidationRequest -from sqlbuild.spec.contracts.models import SchemaAuditInstance, SourceLocation +from sqlbuild.spec.contracts.models import SchemaAuditInstance -_UNKNOWN_TYPE: str = "UNKNOWN" _EXPRESSION_AUDIT: str = "expression_is_true" _RELATIONSHIPS_AUDIT: str = "relationships" _ACCEPTED_VALUES_AUDIT: str = "accepted_values" -_SELECT_KIND: str = "select" -_FUNCTION_KIND: str = "function" -_CURSOR_FAMILIES: dict[str, frozenset[TypeFamily]] = { - "integer": frozenset({TypeFamily.INTEGER, TypeFamily.DECIMAL}), - "timestamp": frozenset({TypeFamily.TIMESTAMP, TypeFamily.DATETIME, TypeFamily.DATE}), - "date": frozenset({TypeFamily.TIMESTAMP, TypeFamily.DATETIME, TypeFamily.DATE}), -} - - -def get_semantic_metadata_diagnostics( - *, - project: CompiledProject, - profile: ExpressionInferenceProfile, - resource_sql_analysis: bool = True, -) -> tuple[CompilerDiagnostic, ...]: - """Reject metadata names only where absence can be proven; resource SQL only when analysed.""" - - if not project.settings.sql_analysis: - return () - shapes: dict[str, dict[str, str]] = semantic_shapes(project=project, profile=profile) - functions: dict[str, CompiledFunction] = { - function.name.casefold(): function for function in project.functions - } - diagnostics: list[CompilerDiagnostic] = [] - for model in project.models: - if model.config.values.get("sql_analysis") is False or not model.binding_validated: - continue - diagnostics.extend( - _function_errors( - model=model, - functions=functions, - shapes=shapes, - profile=profile, - ) - ) - shape: dict[str, str] | None = shapes.get(model.name) - if shape is None: - continue - diagnostics.extend( - _audit_errors(model=model, project=project, shape=shape, shapes=shapes, profile=profile) - ) - values: dict[str, object] = model.config.values - references: list[tuple[str, str, dict[str, str]]] = [] - for key in ("unique_key", "cursor", "partition_column", "row_diff_exclude_columns"): - references.extend((key, name, shape) for name in _names(values.get(key))) - custom: object = values.get("config") - if isinstance(custom, dict): - references.extend( - ("partition_column", name, shape) - for name in _names(cast(dict[str, object], custom).get("partition_column")) - ) - tolerances: object = values.get("row_diff_tolerances") - by_column: object = ( - cast(dict[str, object], tolerances).get("by_column") - if isinstance(tolerances, dict) - else None - ) - if isinstance(by_column, dict): - references.extend(("row_diff_tolerances", str(name), shape) for name in by_column) - cursor_inputs: object = values.get("cursor_inputs") - if isinstance(cursor_inputs, dict): - for upstream, columns in cursor_inputs.items(): - upstream_shape: dict[str, str] | None = shapes.get(str(upstream)) - if upstream_shape is not None: - references.extend( - (f"cursor_inputs {upstream}", name, upstream_shape) - for name in _names(columns) - ) - for key, name, reference_shape in references: - if name.casefold() not in {column.casefold() for column in reference_shape}: - diagnostics.append( - _model_error( - model=model, - code="B300", - name=name, - message=f"{key} references unknown column '{name}'", - ) - ) - cursor: object = values.get("cursor") - cursor_type: object = values.get("cursor_type") - if isinstance(cursor, str) and isinstance(cursor_type, str): - actual: str = next( - (value for key, value in shape.items() if key.casefold() == cursor.casefold()), - "UNKNOWN", - ) - if cursor_type.lower() in _CURSOR_FAMILIES: - family: TypeFamily = normalize_type( - type_sql=actual, dialect=profile.sql_analysis_dialect - ).family - if ( - family != TypeFamily.OTHER - and family not in _CURSOR_FAMILIES[cursor_type.lower()] - ): - diagnostics.append( - _model_error( - model=model, - code="B301", - name=cursor, - message=( - f"cursor_type {cursor_type} does not match " - f"column '{cursor}' type {actual}" - ), - ) - ) - for source in project.sources: - cursor = source.source_entry.cursor_column - shape = shapes.get(source.name) - if ( - cursor - and shape is not None - and cursor.casefold() not in {name.casefold() for name in shape} - ): - line: int - column: int - line, column = _text_position(text=source.source_file.contents, name=cursor) - diagnostics.append( - CompilerDiagnostic( - phase=DiagnosticPhase.COMPILE, - severity=DiagnosticSeverity.ERROR, - code="B300", - message=f"cursor_column references unknown column '{cursor}'", - resource_type=CompiledResourceType.SOURCE, - resource_name=source.name, - path=source.source_file.relative_path, - line=line, - column=column, - ) - ) - diagnostics.extend(_sql_test_errors(project=project, shapes=shapes, profile=profile)) - if resource_sql_analysis: - diagnostics.extend( - get_resource_sql_diagnostics(project=project, shapes=shapes, profile=profile) - ) - return tuple(diagnostics) def _audit_errors( @@ -293,22 +147,6 @@ def _audit_errors( return tuple(diagnostics) -def _different_argument_families( - *, left: str, right: str, profile: ExpressionInferenceProfile -) -> bool: - families: set[TypeFamily] = { - normalize_type(type_sql=value, dialect=profile.sql_analysis_dialect).family - for value in (left, right) - } - different_families: bool = ( - len(families) > 1 - and TypeFamily.OTHER not in families - and not families <= {TypeFamily.INTEGER, TypeFamily.DECIMAL, TypeFamily.FLOAT} - and not families <= {TypeFamily.TIMESTAMP, TypeFamily.DATE, TypeFamily.DATETIME} - ) - return different_families - - @lru_cache(maxsize=256) def _comparison_result(*, left: str, right: str, dialect: str | None) -> SqlBindingResult: return get_schema_validations( @@ -349,57 +187,6 @@ def _accepted_values_result( )[0] -def _sql_test_errors( - *, - project: CompiledProject, - shapes: dict[str, dict[str, str]], - profile: ExpressionInferenceProfile, -) -> tuple[CompilerDiagnostic, ...]: - diagnostics: list[CompilerDiagnostic] = [] - columns_by_sql: dict[str, tuple[str, ...]] = _sql_test_columns(project=project, profile=profile) - for test in project.sql_tests: - if not isinstance(test.payload, CompiledModelSqlTestPayload): - continue - for cte in (*test.payload.authored_ctes, *test.payload.expected_ctes): - match: re.Match[str] | None = re.match( - r"__(?:expected|ref|source|seed)__(.+)$", cte.name - ) - shape: dict[str, str] | None = shapes.get(match.group(1)) if match else None - if shape is None: - continue - available: set[str] = {name.casefold() for name in shape} - for column_name in columns_by_sql[cte.sql_body]: - if column_name.casefold() not in available: - line: int - column_position: int - line, column_position = _text_position( - text=test.test_file.contents, - name=column_name, - offset=max(test.test_file.contents.find(cte.name), 0), - ) - diagnostics.append( - CompilerDiagnostic( - phase=DiagnosticPhase.COMPILE, - severity=DiagnosticSeverity.ERROR, - code="B302", - message=f"SQL test '{cte.name}' names unknown column '{column_name}'", - resource_type=CompiledResourceType.SQL_TEST, - resource_name=test.name, - path=test.test_file.relative_path, - line=line, - column=column_position, - location=SourceLocation( - path=test.test_file.relative_path, - line=line, - column=column_position, - end_line=line, - end_column=column_position + len(column_name), - ), - ) - ) - return tuple(diagnostics) - - def _sql_test_columns( *, project: CompiledProject, profile: ExpressionInferenceProfile ) -> dict[str, tuple[str, ...]]: @@ -442,182 +229,6 @@ def _names(value: object) -> tuple[str, ...]: return () -def _function_errors( - *, - model: CompiledModel, - functions: dict[str, CompiledFunction], - shapes: dict[str, dict[str, str]], - profile: ExpressionInferenceProfile, -) -> tuple[CompilerDiagnostic, ...]: - if not functions or not any( - reference.ref_kind in {SqlReferenceKind.UDF, SqlReferenceKind.TABLE_FUNCTION} - for reference in model.references - ): - return () - module: Any = import_polyglot_sql() - request: SqlSchemaValidationRequest = get_complete_schema_binding_request( - query_sql=model.query_sql, - placeholders=None, - dialect=profile.sql_analysis_dialect, - binding_schema=shapes, - ) - try: - parsed: Any = module.parse_one(request.sql, dialect=profile.sql_analysis_dialect) - except module.PolyglotError: - return () - calls: list[tuple[Any, Any, dict[str, Any], CompiledFunction]] = list( - _declared_function_calls(root=parsed, functions=functions) - ) - column_selects: dict[int, Any] = { - id(select): select - for _, select, payload, function in calls - if select is not None and _resolves_argument_columns(payload=payload, function=function) - } - relations_by_select: dict[int, list[dict[str, Any]]] = { - key: _select_relations(select=select) for key, select in column_selects.items() - } - table_names: set[str] = set() - for relations in relations_by_select.values(): - table_names.update(map(_relation_table_name, relations)) - folded_shapes: dict[str, dict[str, list[str]]] = { - table_name: _folded_columns(shapes[table_name]) - for table_name in table_names - if table_name in shapes - } - diagnostics: list[CompilerDiagnostic] = [] - for call, select, payload, function in calls: - name: str = str(payload.get("name", "")).removeprefix("__sqlbuild_udf_") - arguments: list[dict[str, Any]] = payload.get("args", []) - if len(arguments) != len(function.arguments): - diagnostics.append( - _model_error( - model=model, - code="B102", - name=name, - message=( - f"Function '{name}' expects {len(function.arguments)} arguments " - f"but received {len(arguments)}" - ), - ) - ) - continue - argument_expressions: list[Any] = call.expressions - for argument, expression, declaration in zip( - arguments, argument_expressions, function.arguments, strict=True - ): - actual: str = ( - _polyglot_expression_type(expression=expression, inference_profile=profile) - or _UNKNOWN_TYPE - ) - literal: Any = argument.get("literal") - column: Any = argument.get("column") - if literal: - actual = {"string": "VARCHAR", "number": "DOUBLE", "boolean": "BOOLEAN"}.get( - literal.get("literal_type"), "UNKNOWN" - ) - elif column: - actual = _argument_column_type( - column=column, - relations=None if select is None else relations_by_select[id(select)], - folded_shapes=folded_shapes, - ) - if actual != _UNKNOWN_TYPE and _different_argument_families( - left=actual, right=declaration.type, profile=profile - ): - diagnostics.append( - _model_error( - model=model, - code="B301", - severity=DiagnosticSeverity.ERROR, - name=name, - message=( - f"Function '{name}' argument '{declaration.name}' " - f"expects {declaration.type}, received {actual}; " - "convert the argument explicitly" - ), - ) - ) - return tuple(diagnostics) - - -def _declared_function_calls( - *, root: Any, functions: dict[str, CompiledFunction] -) -> Iterator[tuple[Any, Any, dict[str, Any], CompiledFunction]]: - """Yield calls of declared functions with their nearest select and serialised payload.""" - - for call, select in _function_scopes(root): - payload: dict[str, Any] = call.to_dict().get("function", {}) - name: str = str(payload.get("name", "")).removeprefix("__sqlbuild_udf_") - function: CompiledFunction | None = functions.get(name.casefold()) - if function is not None: - yield call, select, payload, function - - -def _resolves_argument_columns(*, payload: dict[str, Any], function: CompiledFunction) -> bool: - arguments: list[dict[str, Any]] = payload.get("args", []) - return len(arguments) == len(function.arguments) and any( - not argument.get("literal") and argument.get("column") for argument in arguments - ) - - -def _function_scopes(root: Any) -> Iterator[tuple[Any, Any]]: - pending: list[tuple[Any, Any]] = [(root, None)] - node: Any - select: Any - while pending: - node, select = pending.pop() - if node.kind == _SELECT_KIND: - select = node - if node.kind == _FUNCTION_KIND: - yield node, select - pending.extend((child, select) for child in reversed(node.children())) - - -def _select_relations(*, select: Any) -> list[dict[str, Any]]: - payload: dict[str, Any] = select.to_dict().get("select", {}) - relations: list[dict[str, Any]] = list((payload.get("from") or {}).get("expressions", [])) - relations.extend(join.get("this", {}) for join in payload.get("joins", [])) - return relations - - -def _relation_table_name(relation: dict[str, Any]) -> str: - return str(relation.get("table", {}).get("name", {}).get("name", "")) - - -def _folded_columns(shape: dict[str, str]) -> dict[str, list[str]]: - """Index column types by case-folded name, preserving declaration order per name.""" - - folded: dict[str, list[str]] = {} - for key, value in shape.items(): - folded.setdefault(key.casefold(), []).append(value) - return folded - - -def _argument_column_type( - *, - column: dict[str, Any], - relations: list[dict[str, Any]] | None, - folded_shapes: dict[str, dict[str, list[str]]], -) -> str: - if relations is None: - return _UNKNOWN_TYPE - qualifier: str | None = (column.get("table") or {}).get("name") - name: str = str(column.get("name", {}).get("name", "")) - folded_name: str = name.casefold() - candidates: list[str] = [] - for relation in relations: - table: dict[str, Any] = relation.get("table", {}) - table_name: str = _relation_table_name(relation) - alias: str = str((table.get("alias") or {}).get("name", table_name)) - if qualifier is not None and qualifier.casefold() != alias.casefold(): - continue - shape: dict[str, list[str]] | None = folded_shapes.get(table_name) - if shape is None: - return _UNKNOWN_TYPE - candidates.extend(shape.get(folded_name, ())) - return candidates[0] if len(candidates) == 1 else _UNKNOWN_TYPE - - def _model_error( *, model: CompiledModel, diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/details.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/details.py index 20fd94da22..86257468d3 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/details.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/details.py @@ -1,57 +1,13 @@ -"""Explain semantic diagnostics using the query's actual input schemas.""" +"""Column suggestions and unknown-column parsing for resource SQL diagnostics.""" from __future__ import annotations import re -from bisect import bisect_right -from dataclasses import replace from typing import Any -from sqlbuild.compiler.compile._helpers.analysis.compact import get_complete_schema_binding_request -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes -from sqlbuild.compiler.compile._helpers.diagnostics.help import ( - comparison_help, - semantic_help, -) -from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject, CompilerDiagnostic -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.compiler.sql_analysis.models import SqlSchemaValidationRequest -from sqlbuild.spec.contracts.models import SourceLocation - -_UNKNOWN_COLUMN: str = "B002" -_COMPARISON: str = "B217" _MIN_ABBREVIATION_LENGTH: int = 3 _MAX_EDIT_DISTANCE: int = 2 -_DISPLAY_LIMIT: int = 10 -_QUALIFIER_SEPARATOR: str = "." -_COLUMN_KIND: str = "column" -_TYPE_CODES: frozenset[str] = frozenset(f"B21{i}" for i in range(10)) -_TYPE_WORDS: str = ( - r"\b(timestamp|integer|varchar|boolean|date|interval|double|float|decimal|numeric|" - r"bigint|smallint|text|time|string|binary|array|struct)\b" -) -type _LineIndex = tuple[tuple[str, ...], tuple[int, ...]] -type _BinaryIndex = tuple[tuple[int, ...], tuple[re.Match[str], ...]] _MISSING: re.Pattern[str] = re.compile(r"Unknown column '([^']+)'(?: in table '([^']+)')?") -_OPERAND: str = ( - r"(?:TIMESTAMP\s+'[^']*'|DATE\s+'[^']*'|'(?:[^']|'')*'|-?\d+(?:\.\d+)?|" - r"[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)?)" -) -_BINARY: re.Pattern[str] = re.compile( - rf"(?P{_OPERAND})\s*(?:>=|<=|<>|!=|=|>|<|\+|\*|/|-)\s*(?P{_OPERAND})", - re.IGNORECASE, -) - - -def sentence_message(message: str) -> str: - text: str = re.sub(r" \(context: [^)]*\)$", "", message) - pieces: list[str] = re.split(r"('[^']*')", text) - for index in range(0, len(pieces), 2): - pieces[index] = re.sub( - _TYPE_WORDS, lambda match: match.group().upper(), pieces[index], flags=re.IGNORECASE - ) - return "".join(pieces) def column_distance(*, left: str, right: str) -> int: @@ -95,253 +51,3 @@ def closest_column(*, name: str, columns: dict[str, str]) -> str | None: def missing_column(message: str) -> tuple[str, str | None] | None: match: re.Match[str] | None = _MISSING.search(message) return (match.group(1), match.group(2)) if match else None - - -def input_aliases(*, model: CompiledModel, dialect: str | None) -> dict[str, str]: - parsed: Any = _parsed_model(model=model, dialect=dialect) - if parsed is None: - return {} - aliases: dict[str, set[str]] = {} - for table in parsed.find_all("table"): - value: dict[str, Any] = table.to_dict().get("table", {}) - name: str = str(value.get("name", {}).get("name", "")) - aliases.setdefault(name, set()).add(name) - alias: dict[str, Any] = value.get("alias") or {} - if alias: - aliases.setdefault(str(alias["name"]), set()).add(name) - return {alias: next(iter(names)) for alias, names in aliases.items() if len(names) == 1} - - -def unaliased_output_columns(*, model: CompiledModel, dialect: str | None) -> set[str]: - """Only an unaliased missing projection can lose its corrected output name.""" - parsed: Any = _parsed_model(model=model, dialect=dialect) - if parsed is None: - return set() - projections: list[dict[str, Any]] = parsed.to_dict().get("select", {}).get("expressions", []) - return { - str(expression["column"]["name"]["name"]) - for expression in projections - if _COLUMN_KIND in expression - } - - -def _parsed_model(*, model: CompiledModel, dialect: str | None) -> Any: - request: SqlSchemaValidationRequest = get_complete_schema_binding_request( - query_sql=model.query_sql, placeholders=None, dialect=dialect, binding_schema={} - ) - module: Any = import_polyglot_sql() - try: - return module.parse_one(request.sql, dialect=dialect) - except module.PolyglotError: - return None - - -def explain_diagnostics(project: CompiledProject) -> CompiledProject: - """Enrich errors only; successful models require no extra SQL parsing.""" - if not project.diagnostics: - return project - shapes: dict[str, dict[str, str]] = semantic_shapes(project=project) - models: dict[str, CompiledModel] = {model.name: model for model in project.models} - aliases: dict[str, dict[str, str]] = {} - line_indexes: dict[str, _LineIndex] = {} - binary_indexes: dict[str, _BinaryIndex] = {} - diagnostics: list[CompilerDiagnostic] = [] - diagnostic: CompilerDiagnostic - for diagnostic in project.diagnostics: - model: CompiledModel | None = models.get(diagnostic.resource_name or "") - if model is not None and diagnostic.code in {_UNKNOWN_COLUMN, *_TYPE_CODES}: - if model.name not in aliases: - aliases[model.name] = input_aliases( - model=model, dialect=project.sql_analysis_dialect - ) - line_indexes[model.name] = _line_index(model.authored_sql) - if diagnostic.code in _TYPE_CODES and model.name not in binary_indexes: - binary_indexes[model.name] = _binary_index(model.authored_sql) - diagnostic = _explain_model( - diagnostic=diagnostic, - model=model, - aliases=aliases[model.name], - shapes=shapes, - dialect=project.sql_analysis_dialect, - lines=line_indexes[model.name], - binaries=binary_indexes.get(model.name, ((), ())), - ) - elif semantic_help(diagnostic.code) is not None: - diagnostic = replace( - diagnostic, - message=sentence_message(diagnostic.message), - help=diagnostic.help or semantic_help(diagnostic.code), - ) - diagnostics.append(diagnostic) - return replace( - project, - diagnostics=tuple(diagnostics), - models=update_binding_models(models=project.models, diagnostics=tuple(diagnostics)), - ) - - -def update_binding_models( - *, models: tuple[CompiledModel, ...], diagnostics: tuple[CompilerDiagnostic, ...] -) -> tuple[CompiledModel, ...]: - """Keep model binding facts distinct from metadata and SQL-test diagnostics.""" - by_model: dict[str, list[CompilerDiagnostic]] = {} - for diagnostic in diagnostics: - if diagnostic.resource_type == CompiledResourceType.MODEL: - by_model.setdefault(diagnostic.resource_name or "", []).append(diagnostic) - updated: list[CompiledModel] = [] - for model in models: - codes: set[str] = {item.code for item in model.binding_diagnostics} - bindings: tuple[CompilerDiagnostic, ...] = tuple( - item for item in by_model.get(model.name, ()) if item.code in codes - ) - updated.append(replace(model, binding_diagnostics=bindings)) - return tuple(updated) - - -def _line_index(sql: str) -> _LineIndex: - """Index authored lines once per model so each diagnostic maps positions in O(log n).""" - starts: list[int] = [0] - for line in sql.splitlines(keepends=True): - starts.append(starts[-1] + len(line)) - return tuple(sql.splitlines()), tuple(starts) - - -def _binary_index(sql: str) -> _BinaryIndex: - matches: tuple[re.Match[str], ...] = tuple(_BINARY.finditer(sql)) - return tuple(match.start() for match in matches), matches - - -def _line_offset(*, lines: _LineIndex, line: int, column: int) -> int: - starts: tuple[int, ...] = lines[1] - return starts[min(max(line - 1, 0), len(starts) - 1)] + column - 1 - - -def _binary_at(*, binaries: _BinaryIndex, offset: int) -> re.Match[str] | None: - """Matches are ordered and disjoint, so only the last one starting at or before can match.""" - position: int = bisect_right(binaries[0], offset) - 1 - if position < 0: - return None - match: re.Match[str] = binaries[1][position] - return match if offset < match.end() else None - - -def _explain_model( - *, - diagnostic: CompilerDiagnostic, - model: CompiledModel, - aliases: dict[str, str], - shapes: dict[str, dict[str, str]], - dialect: str | None, - lines: _LineIndex, - binaries: _BinaryIndex, -) -> CompilerDiagnostic: - message: str = sentence_message(diagnostic.message) - help_text: str | None = semantic_help(diagnostic.code) - if diagnostic.code == _COMPARISON: - help_text = comparison_help( - types=tuple( - value.upper() - for value in re.findall(_TYPE_WORDS, diagnostic.message, re.IGNORECASE) - ), - dialect=dialect, - ) - notes: list[str] = list(diagnostic.notes) - location: SourceLocation | None = diagnostic.location - missing: tuple[str, str | None] | None = missing_column(diagnostic.message) - if missing is not None: - name, table = missing - columns: dict[str, str] = shapes.get(table or "", {}) - suggestion: str | None = closest_column(name=name, columns=columns) - help_text = f"did you mean '{suggestion}'?" if suggestion else None - message = f"Unknown column '{name}'" + (f" in {table}" if table else "") - if location is not None: - prefix: str = lines[0][location.line - 1][: location.column - 1] - qualifier: re.Match[str] | None = re.search( - r'(?:"((?:[^"]|"")+)"|([A-Za-z_]\w*))\.$', prefix - ) - alias: str | None = ( - (qualifier.group(1) or qualifier.group(2)).replace('""', '"') if qualifier else None - ) - if alias and aliases.get(alias) == table: - message += f" (as {alias})" - available: list[str] = ordered_columns(name=name, columns=columns) - if available: - suffix: str = ( - f", and {len(available) - _DISPLAY_LIMIT} more" - if len(available) > _DISPLAY_LIMIT - else "" - ) - notes.insert(0, f"{table} has: {', '.join(available[:10])}{suffix}") - if diagnostic.code in _TYPE_CODES and location is not None: - offset: int = _line_offset(lines=lines, line=location.line, column=location.column) - binary: re.Match[str] | None = _binary_at(binaries=binaries, offset=offset) - if binary: - left: str = binary.group("left") - right: str = binary.group("right") - types: tuple[str | None, str | None] = ( - _operand_type(text=left, aliases=aliases, shapes=shapes), - _operand_type(text=right, aliases=aliases, shapes=shapes), - ) - if all(types): - notes.append(f"{left} is {types[0]}, {right} is {types[1]}") - if diagnostic.code == _COMPARISON: - help_text = comparison_help( - types=tuple(value for value in types if value), dialect=dialect - ) - end: int = binary.end() - if location.end_line is not None and location.end_column is not None: - native_end: int = _line_offset( - lines=lines, line=location.end_line, column=location.end_column - ) - end = max(end, native_end) - begin: int = min(binary.start(), offset) - location = replace( - location, - line=model.authored_sql.count("\n", 0, begin) + 1, - column=begin - model.authored_sql.rfind("\n", 0, begin), - end_line=model.authored_sql.count("\n", 0, end) + 1, - end_column=end - model.authored_sql.rfind("\n", 0, end), - ) - else: - operand: re.Match[str] | None = re.compile(_OPERAND, re.IGNORECASE).match( - model.authored_sql, offset - ) - if operand: - operand_type: str | None = _operand_type( - text=operand.group(), aliases=aliases, shapes=shapes - ) - if operand_type: - notes.append(f"{operand.group()} is {operand_type}") - return replace( - diagnostic, - message=message, - help=help_text, - notes=tuple(notes), - location=location, - line=location.line if location is not None else diagnostic.line, - column=location.column if location is not None else diagnostic.column, - ) - - -def _operand_type( - *, text: str, aliases: dict[str, str], shapes: dict[str, dict[str, str]] -) -> str | None: - temporal: re.Match[str] | None = re.match(r"(TIMESTAMP|DATE)\s*'", text, re.IGNORECASE) - if temporal: - return temporal.group(1).upper() - if re.fullmatch(r"-?\d+", text): - return "INTEGER" - if re.fullmatch(r"-?\d+\.\d+", text): - return "DECIMAL" - if text.startswith("'"): - return "VARCHAR" - if _QUALIFIER_SEPARATOR in text: - alias, column = text.split(".", 1) - value: str | None = shapes.get(aliases.get(alias, alias), {}).get(column) - return value.upper() if value else None - candidates: set[str] = { - shapes[table][text].upper() - for table in set(aliases.values()) - if table in shapes and text in shapes[table] - } - return next(iter(candidates)) if len(candidates) == 1 else None diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/help.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/help.py index 904fa0380c..1ccfd4bf25 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/help.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/help.py @@ -1,8 +1,5 @@ """Code-owned semantic remediation; no catch-all help for unrelated diagnostics.""" -_TIMESTAMP: str = "TIMESTAMP" -_DATE: str = "DATE" -_BIGQUERY: str = "bigquery" _SEMANTIC_HELP: dict[str, str] = { "B003": "qualify this column with the intended input alias", "B004": "give each referenced relation a visible alias in this scope", @@ -33,20 +30,3 @@ def semantic_help_catalogue() -> dict[str, str]: """Return the code-owned remediation catalogue.""" return dict(_SEMANTIC_HELP) - - -def semantic_help(code: str) -> str | None: - """Return help only for a code with its own remediation.""" - return _SEMANTIC_HELP.get(code) - - -def comparison_help(*, types: tuple[str, ...], dialect: str | None) -> str: - """Offer a concrete literal for a proven temporal comparison mismatch.""" - if any(_TIMESTAMP in value for value in types): - literal: str = "TIMESTAMP '2026-04-01'" - if dialect == _BIGQUERY: - literal = "TIMESTAMP '2026-04-01 00:00:00+00'" - return f"compare with a timestamp, for example {literal}" - if _DATE in types: - return "compare with a date, for example DATE '2026-04-01'" - return _SEMANTIC_HELP["B217"] diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py index 6f83b35163..212d9a5dd2 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py @@ -1,41 +1,16 @@ -"""Recover poisoned output names without suppressing unrelated downstream errors.""" +"""Complete semantic diagnostics: type recovery, metadata checks, recovery and explanations.""" from __future__ import annotations -import re -from collections import Counter -from dataclasses import replace from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.assembly.metadata_validation import ( - get_semantic_metadata_diagnostics, -) -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes -from sqlbuild.compiler.compile._helpers.diagnostics.details import ( - closest_column, - explain_diagnostics, - missing_column, - unaliased_output_columns, - update_binding_models, -) -from sqlbuild.compiler.compile._helpers.diagnostics.sql_analysis_opt_outs import ( - reject_unneeded_sql_analysis_opt_outs, -) -from sqlbuild.compiler.compile._helpers.diagnostics.type_recovery import recover_output_types -from sqlbuild.compiler.compile.models import ( - CompiledModel, - CompiledProject, - CompilerDiagnostic, -) +from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.semantic_checks.main._complete_native_semantic_diagnostics import ( complete_native_semantic_diagnostics, ) from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic -_MISSING_CODE: str = "B002" -_TEST_CODE: str = "B302" - def complete_semantic_diagnostics( *, @@ -46,116 +21,11 @@ def complete_semantic_diagnostics( native_session: Any | None = None, ) -> CompiledProject: """Recover type facts before metadata checks, then explain retained root diagnostics.""" - native_project: CompiledProject | None = complete_native_semantic_diagnostics( + + return complete_native_semantic_diagnostics( project=project, profile=profile, binding_results=binding_results, resource_sql_analysis=resource_sql_analysis, session=native_session, ) - if native_project is not None: - return native_project - project = recover_output_types(project=project, binding_results=binding_results) - project = replace( - project, - diagnostics=( - *project.diagnostics, - *get_semantic_metadata_diagnostics( - project=project, profile=profile, resource_sql_analysis=resource_sql_analysis - ), - ), - ) - return reject_unneeded_sql_analysis_opt_outs(explain_diagnostics(recover_diagnostics(project))) - - -def recover_diagnostics(project: CompiledProject) -> CompiledProject: - """Recover poisoned uses while retaining independent errors on unaffected outputs.""" - roots: list[CompilerDiagnostic] = [ - item for item in project.diagnostics if item.code == _MISSING_CODE - ] - if not roots: - return project - shapes: dict[str, dict[str, str]] = semantic_shapes(project=project) - models: dict[str, CompiledModel] = {model.name: model for model in project.models} - poisoned: dict[tuple[str, str], CompilerDiagnostic] = {} - origins: dict[CompilerDiagnostic, tuple[str, str]] = {} - projections: dict[str, set[str]] = {} - inferred_names: dict[str, set[str]] = {} - upstream_sources: dict[str, dict[str, set[tuple[str, str]]]] = {} - for diagnostic in roots: - model: CompiledModel | None = models.get(diagnostic.resource_name or "") - missing: tuple[str, str | None] | None = missing_column(diagnostic.message) - if model is None or missing is None: - continue - column, table = missing - if model.name not in inferred_names: - inferred_names[model.name] = {item.name for item in model.inferred_columns or ()} - if column not in inferred_names[model.name]: - continue - if model.name not in projections: - projections[model.name] = unaliased_output_columns( - model=model, dialect=project.sql_analysis_dialect - ) - if column not in projections[model.name]: - continue - if model.name not in upstream_sources: - upstream_sources[model.name] = _upstream_sources_by_output(model=model) - if table is None or (table, column) not in upstream_sources[model.name].get(column, set()): - continue - suggestion: str | None = closest_column(name=column, columns=shapes.get(table or "", {})) - if suggestion is not None: - poisoned[(model.name, suggestion)] = diagnostic - origins[diagnostic] = model.name, suggestion - for _ in project.models: - previous: int = len(poisoned) - for model in project.models: - for column in model.fast_lineage_columns or (): - for source in column.upstream_columns: - root: CompilerDiagnostic | None = poisoned.get( - (source.resource_name, source.column_name) - ) - if root is not None: - poisoned.setdefault((model.name, column.output_column), root) - if len(poisoned) == previous: - break - skipped: Counter[tuple[CompilerDiagnostic, str, str]] = Counter() - kept: list[CompilerDiagnostic] = [] - for diagnostic in project.diagnostics: - missing = missing_column(diagnostic.message) if diagnostic.code == _MISSING_CODE else None - target: tuple[str, str] | None = ( - (missing[1], missing[0]) if missing and missing[1] else None - ) - if diagnostic.code == _TEST_CODE: - match: re.Match[str] | None = re.search( - r"'__(?:expected|ref)__(.+)' names unknown column '([^']+)'", diagnostic.message - ) - if match: - target = match.group(1), match.group(2) - root = poisoned.get(target) if target is not None else None - if root is not None and root != diagnostic and target is not None: - skipped[(root, *origins[root])] += 1 - else: - kept.append(diagnostic) - notes: dict[CompilerDiagnostic, list[str]] = {} - for (root, model_name, column_name), count in skipped.items(): - notes.setdefault(root, []).append( - f"{count} downstream uses of {model_name}.{column_name} " - "were not checked because of this error" - ) - diagnostics: tuple[CompilerDiagnostic, ...] = tuple( - replace(item, notes=(*item.notes, *notes.get(item, ()))) for item in kept - ) - return replace( - project, - diagnostics=diagnostics, - models=update_binding_models(models=project.models, diagnostics=diagnostics), - ) - - -def _upstream_sources_by_output(*, model: CompiledModel) -> dict[str, set[tuple[str, str]]]: - sources: dict[str, set[tuple[str, str]]] = {} - for output in model.fast_lineage_columns or (): - sources.setdefault(output.output_column, set()).update( - (source.resource_name, source.column_name) for source in output.upstream_columns - ) - return sources diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py index 58176d71a6..5d5f64545f 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py @@ -4,7 +4,6 @@ import re from collections import Counter -from dataclasses import replace from pathlib import Path from sqlbuild.compiler.compile._helpers.analysis.validation import ( @@ -15,8 +14,6 @@ from sqlbuild.compiler.compile.constants import UNNEEDED_SQL_ANALYSIS_OPT_OUT_CODE from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( - CompiledModel, - CompiledProject, CompilerDiagnostic, SqlAnalysisOptOutRequest, ) @@ -42,7 +39,6 @@ from sqlbuild.spec.contracts.models import SettingsConfig, SourceLocation _LEGACY_KEY: str = "sql_validation" -_SEMANTIC_CODE_PREFIX: str = "B" _OPT_OUT_KEYS: tuple[str, ...] = (SQL_ANALYSIS_CONFIG_KEY, _LEGACY_KEY) _HOOK_KEYS: tuple[str, ...] = ("pre_hooks", "post_hooks") _OPT_OUT_KEY_PATTERN: re.Pattern[str] = re.compile(r"\b(?:sql_analysis|sql_validation)\b") @@ -108,48 +104,6 @@ def _parses(request: SqlAnalysisOptOutRequest) -> bool: return True -def reject_unneeded_sql_analysis_opt_outs(project: CompiledProject) -> CompiledProject: - """Replace each rejected opt-out model's findings with one error that counts them.""" - - rejected: dict[str, CompiledModel] = { - model.name: model - for model in project.models - if model.rejected_sql_analysis_opt_out is not None - } - if not rejected: - return project - hidden: dict[str, list[CompilerDiagnostic]] = {name: [] for name in rejected} - kept: list[CompilerDiagnostic] = [] - for diagnostic in project.diagnostics: - if ( - diagnostic.resource_name in hidden - and diagnostic.resource_type in (None, CompiledResourceType.MODEL) - and diagnostic.code.startswith(_SEMANTIC_CODE_PREFIX) - ): - hidden[diagnostic.resource_name].append(diagnostic) - else: - kept.append(diagnostic) - kept.extend( - _opt_out_diagnostic(model=model, hidden=tuple(hidden[name])) - for name, model in sorted(rejected.items()) - ) - return replace(project, diagnostics=tuple(kept)) - - -def _opt_out_diagnostic( - *, model: CompiledModel, hidden: tuple[CompilerDiagnostic, ...] -) -> CompilerDiagnostic: - location: SourceLocation | None = model.rejected_sql_analysis_opt_out - return unneeded_opt_out_diagnostic( - resource_type=CompiledResourceType.MODEL, - kind="model", - name=model.name, - location=location, - hidden=hidden, - path_default=location is not None and location.path.name == PROJECT_CONFIG_FILENAME, - ) - - def unneeded_opt_out_diagnostic( *, resource_type: CompiledResourceType, diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/type_recovery.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/type_recovery.py deleted file mode 100644 index b0e3b165a2..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/type_recovery.py +++ /dev/null @@ -1,299 +0,0 @@ -"""Propagate unknown output types from located root errors without opening other columns.""" - -from bisect import bisect_right -from collections import Counter, deque -from dataclasses import replace -from typing import Any - -from sqlbuild.compiler.compile._helpers.analysis.compact import get_complete_schema_binding_request -from sqlbuild.compiler.compile._helpers.assembly.native_declarations import ( - known_declared_types, - known_function_names, -) -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes -from sqlbuild.compiler.compile._helpers.diagnostics.details import update_binding_models -from sqlbuild.compiler.compile.models import ( - CompiledModel, - CompiledProject, - CompilerDiagnostic, - InferredColumn, -) -from sqlbuild.compiler.sql_analysis.main._identifier_case import ignores_quoted_case -from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.compiler.sql_analysis.models import ( - SqlBindingDiagnostic, - SqlBindingResult, - SqlSchemaValidationRequest, -) - -_SELECT: str = "SELECT" -_PROJECTION_END: frozenset[str] = frozenset( - { - "FROM", - "WHERE", - "GROUP_BY", - "HAVING", - "QUALIFY", - "ORDER_BY", - "LIMIT", - "UNION", - "INTERSECT", - "EXCEPT", - } -) -_OPEN: str = "(" -_CLOSE: str = ")" -_COMMA: str = "," - - -def recover_output_types( - *, project: CompiledProject, binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]] -) -> CompiledProject: - """Recheck only failed dependants using unknown types for poisoned lineage outputs.""" - failed: list[CompiledModel] = [model for model in project.models if model.binding_diagnostics] - blocking: list[CompilerDiagnostic] = [] - for model in failed: - blocking.extend(item for item in model.binding_diagnostics if item.is_error) - if not blocking: - return project - shapes: dict[str, dict[str, str]] = semantic_shapes(project=project) - requests: tuple[SqlSchemaValidationRequest, ...] = tuple( - _request(project=project, model=model, shapes=shapes) for model in failed - ) - results: tuple[SqlBindingResult, ...] = tuple( - SqlBindingResult(diagnostics=binding_results.get(model.name, ())) for model in failed - ) - poisoned: dict[tuple[str, str], CompilerDiagnostic] = {} - roots_by_model: dict[str, dict[SqlBindingDiagnostic, CompilerDiagnostic]] = {} - outputs_by_diagnostic: dict[CompilerDiagnostic, set[str]] = {} - for model, request, result in zip(failed, requests, results, strict=True): - roots_by_model[model.name] = _root_bindings(model=model, diagnostics=result.diagnostics) - spans: list[tuple[int, int]] = _projection_spans(sql=request.sql, dialect=request.dialect) - columns: tuple[InferredColumn, ...] = model.inferred_columns or () - if len(spans) != len(columns): - continue - span_starts: list[int] = [start for start, _ in spans] - for raw in result.diagnostics: - if raw.start is None: - continue - root: CompilerDiagnostic | None = roots_by_model[model.name].get(raw) - column: InferredColumn | None = _column_at( - columns=columns, spans=spans, span_starts=span_starts, offset=raw.start - ) - if root is not None and root.is_error and column is not None: - poisoned[(model.name, column.name)] = root - outputs_by_diagnostic.setdefault(root, set()).add(column.name) - if not poisoned: - return project - for _ in project.models: - previous: int = len(poisoned) - for model in project.models: - for output in model.fast_lineage_columns or (): - for source in output.upstream_columns: - root = poisoned.get((source.resource_name, source.column_name)) - if root is not None: - poisoned.setdefault((model.name, output.output_column), root) - if len(poisoned) == previous: - break - unknown_shapes: dict[str, dict[str, str]] = { - name: dict(columns) for name, columns in shapes.items() - } - for name, column_name in poisoned: - if name in unknown_shapes and column_name in unknown_shapes[name]: - unknown_shapes[name][column_name] = "UNKNOWN" - skipped: set[CompilerDiagnostic] = set() - redirected: dict[CompilerDiagnostic, set[CompilerDiagnostic]] = {} - for model, original in zip(failed, results, strict=True): - causes: set[CompilerDiagnostic] = set() - for reference in model.references: - causes.update( - root for (name, _), root in poisoned.items() if name == reference.ref_name - ) - if not causes: - continue - revised: SqlBindingResult = get_schema_validations( - requests=(_request(project=project, model=model, shapes=unknown_shapes),) - )[0] - remaining: set[tuple[str, int | None, int | None]] = { - (item.code, item.start, item.end) for item in revised.diagnostics - } - for raw in original.diagnostics: - diagnostic: CompilerDiagnostic | None = roots_by_model[model.name].get(raw) - if ( - diagnostic is None - or diagnostic in causes - or (raw.code, raw.start, raw.end) in remaining - ): - continue - skipped.add(diagnostic) - redirected[diagnostic] = ( - _output_causes( - model=model, - outputs=outputs_by_diagnostic.get(diagnostic, set()), - poisoned=poisoned, - ) - or causes - ) - counts: Counter[CompilerDiagnostic] = _unchecked_uses( - project=project, poisoned=poisoned, redirected=redirected - ) - diagnostics: tuple[CompilerDiagnostic, ...] = tuple( - replace( - item, - notes=( - *item.notes, - *( - ( - f"{counts[item]} downstream output uses were not type-checked " - "because of this error", - ) - if counts[item] - else () - ), - ), - ) - for item in project.diagnostics - if item not in skipped - ) - models: list[CompiledModel] = [] - for model in update_binding_models(models=project.models, diagnostics=diagnostics): - updated: tuple[InferredColumn, ...] | None = None - if model.inferred_columns is not None: - updated = tuple( - replace(column, type=None) if (model.name, column.name) in poisoned else column - for column in model.inferred_columns - ) - models.append( - replace( - model, - inferred_columns=updated, - unchecked_output_columns=frozenset( - column for name, column in poisoned if name == model.name - ), - ) - ) - return replace(project, models=tuple(models), diagnostics=diagnostics) - - -def _unchecked_uses( - *, - project: CompiledProject, - poisoned: dict[tuple[str, str], CompilerDiagnostic], - redirected: dict[CompilerDiagnostic, set[CompilerDiagnostic]], -) -> Counter[CompilerDiagnostic]: - """Attribute transitive unchecked output uses to retained root errors.""" - counts: Counter[CompilerDiagnostic] = Counter() - for model in project.models: - for output in model.fast_lineage_columns or (): - pending: list[CompilerDiagnostic] = [] - for source in output.upstream_columns: - root: CompilerDiagnostic | None = poisoned.get( - (source.resource_name, source.column_name) - ) - if root is not None: - pending.append(root) - visited: set[CompilerDiagnostic] = set() - while pending: - root = pending.pop() - if root in visited: - continue - visited.add(root) - if root in redirected: - pending.extend(redirected[root]) - else: - counts[root] += 1 - return counts - - -def _root_bindings( - *, model: CompiledModel, diagnostics: tuple[SqlBindingDiagnostic, ...] -) -> dict[SqlBindingDiagnostic, CompilerDiagnostic]: - """Pair repeated messages by occurrence, preserving distinct native spans.""" - pending: dict[tuple[str, str], deque[CompilerDiagnostic]] = {} - for diagnostic in model.binding_diagnostics: - pending.setdefault((diagnostic.code, diagnostic.message), deque()).append(diagnostic) - result: dict[SqlBindingDiagnostic, CompilerDiagnostic] = {} - for raw in diagnostics: - matches: deque[CompilerDiagnostic] | None = pending.get((raw.code, raw.message)) - if matches: - result[raw] = matches.popleft() - return result - - -def _output_causes( - *, model: CompiledModel, outputs: set[str], poisoned: dict[tuple[str, str], CompilerDiagnostic] -) -> set[CompilerDiagnostic]: - """Redirect only the failing projection's lineage instead of every poisoned input.""" - result: set[CompilerDiagnostic] = set() - for output in model.fast_lineage_columns or (): - if output.output_column in outputs: - for source in output.upstream_columns: - root: CompilerDiagnostic | None = poisoned.get( - (source.resource_name, source.column_name) - ) - if root is not None: - result.add(root) - return result - - -def _request( - *, project: CompiledProject, model: CompiledModel, shapes: dict[str, dict[str, str]] -) -> SqlSchemaValidationRequest: - return replace( - get_complete_schema_binding_request( - query_sql=model.query_sql, - placeholders=None, - dialect=project.sql_analysis_dialect, - known_functions=known_function_names(project.functions), - known_types=known_declared_types(functions=project.functions, column_types=shapes), - binding_schema={ - reference.ref_name: shapes.get(reference.ref_name, {}) - for reference in model.references - }, - ), - catalog=project.binding_catalog, - quoted_identifiers_ignore_case=ignores_quoted_case( - connection=project.effective_connection, dialect=project.sql_analysis_dialect - ), - ) - - -def _column_at( - *, - columns: tuple[InferredColumn, ...], - spans: list[tuple[int, int]], - span_starts: list[int], - offset: int, -) -> InferredColumn | None: - """Projection spans are ordered and disjoint, so bisect finds the only candidate.""" - position: int = bisect_right(span_starts, offset) - 1 - if position < 0 or offset >= spans[position][1]: - return None - return columns[position] - - -def _projection_spans(*, sql: str, dialect: str | None) -> list[tuple[int, int]]: - module: Any = import_polyglot_sql() - tokens: list[dict[str, Any]] = module.tokenize(sql, dialect=dialect) - depth: int = 0 - start: int | None = None - spans: list[tuple[int, int]] = [] - for token in tokens: - text: str = token["text"] - if text == _OPEN: - depth += 1 - elif text == _CLOSE: - depth -= 1 - elif depth == 0: - if start is None and token["token_type"] == _SELECT: - start = token["span"]["end"] - elif start is not None and token["token_type"] in _PROJECTION_END: - spans.append((start, token["span"]["start"])) - return spans - elif start is not None and text == _COMMA: - spans.append((start, token["span"]["start"])) - start = token["span"]["end"] - if start is not None: - spans.append((start, len(sql))) - return spans diff --git a/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py b/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py index a4cb6fb255..928ffc73e4 100644 --- a/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py +++ b/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py @@ -10,7 +10,6 @@ _audit_errors, _names, _sql_test_columns, - get_semantic_metadata_diagnostics, ) from sqlbuild.compiler.compile._helpers.assembly.native_declarations import ( known_declared_types, @@ -56,19 +55,6 @@ def revalidation_request( ), ) - @staticmethod - def metadata_diagnostics( - *, - project: CompiledProject, - profile: ExpressionInferenceProfile, - resource_sql_analysis: bool, - ) -> tuple[CompilerDiagnostic, ...]: - """The semantic metadata checks after type recovery.""" - - return get_semantic_metadata_diagnostics( - project=project, profile=profile, resource_sql_analysis=resource_sql_analysis - ) - @staticmethod def metadata_shapes( *, project: CompiledProject, profile: ExpressionInferenceProfile diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py b/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py index 8afb26b9d3..b83283daa8 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py @@ -14,9 +14,7 @@ DiagnosticPhase, DiagnosticSeverity, ) -from sqlbuild.compiler.semantic_checks._helpers.deferrals import record_semantic_deferral from sqlbuild.compiler.semantic_checks._helpers.payloads import diagnostic_ids, lineage_payload -from sqlbuild.compiler.semantic_checks.constants import COMPLETION_DEFERRAL_SITE from sqlbuild.spec.contracts.models import SourceLocation _RECOVERED_CODE: str = "B002" @@ -33,8 +31,8 @@ def complete_native_diagnostics( catalog: object, session: Any | None, session_models: frozenset[str], -) -> CompiledProject | None: - """Return the recovered, explained and opt-out-checked project, or None to use Python.""" +) -> CompiledProject: + """The recovered, explained and opt-out-checked project; a native internal failure raises.""" ids: dict[CompilerDiagnostic, int] = diagnostic_ids(project) recovers: bool = any(item.code == _RECOVERED_CODE for item in project.diagnostics) @@ -50,12 +48,7 @@ def complete_native_diagnostics( ], [(name, list(columns.items())) for name, columns in shapes.items()], ) - deferral, completed, model_bindings, order = _native.complete_semantic_checks( - catalog, request, session - ) - if deferral is not None: - record_semantic_deferral(kind=deferral, site=COMPLETION_DEFERRAL_SITE) - return None + completed, model_bindings, order = _native.complete_semantic_checks(catalog, request, session) diagnostics: list[CompilerDiagnostic] = [ _completed(original=project.diagnostics[row[0]], row=row) for row in completed ] diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/deferrals.py b/src/sqlbuild/compiler/semantic_checks/_helpers/deferrals.py deleted file mode 100644 index 67a2ec2bea..0000000000 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/deferrals.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Debug-only record of semantic completion stages the native engine hands back to Python.""" - -from __future__ import annotations - -import json -import os -from pathlib import Path - -from sqlbuild.compiler.lineage.constants import ANALYSIS_DEFERRAL_RECORD_PREFIX -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR - - -def record_semantic_deferral(*, kind: str, site: str) -> None: - """Append one `{"kind", "site"}` line when the analysis record directory is set.""" - - directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) - if not directory: - return - target: Path = Path(directory) - target.mkdir(parents=True, exist_ok=True) - record: str = json.dumps({"kind": kind, "site": site}) - with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: - _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/metadata.py b/src/sqlbuild/compiler/semantic_checks/_helpers/metadata.py index 46b0366874..5cc53c77be 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/metadata.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/metadata.py @@ -24,10 +24,8 @@ DiagnosticSeverity, ) from sqlbuild.compiler.discovery.constants import SQL_ANALYSIS_CONFIG_KEY -from sqlbuild.compiler.semantic_checks._helpers.deferrals import record_semantic_deferral from sqlbuild.compiler.semantic_checks.constants import ( METADATA_CONFIG_KEYS, - METADATA_DEFERRAL_SITE, METADATA_FUNCTION_REFERENCE_KINDS, ) from sqlbuild.spec.contracts.models import SourceLocation @@ -42,8 +40,8 @@ def native_metadata_diagnostics( profile: ExpressionInferenceProfile, resource_sql_analysis: bool, catalog: object, -) -> tuple[CompilerDiagnostic, ...] | None: - """Return `get_semantic_metadata_diagnostics`' result, or None where Python must check.""" +) -> tuple[CompilerDiagnostic, ...]: + """The project's semantic metadata diagnostics; a native internal failure raises.""" if not project.settings.sql_analysis: return () @@ -78,12 +76,9 @@ def native_metadata_diagnostics( for test in tests ], ) - deferral, model_rows, source_rows, test_rows, fallback_types = ( - _native.check_semantic_metadata_rows(catalog, request) + model_rows, source_rows, test_rows, fallback_types = _native.check_semantic_metadata_rows( + catalog, request ) - if deferral is not None: - record_semantic_deferral(kind=deferral, site=METADATA_DEFERRAL_SITE) - return None for type_sql in fallback_types: _ = normalize_type(type_sql=type_sql, dialect=profile.sql_analysis_dialect) diagnostics: list[CompilerDiagnostic] = [] diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py b/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py index 922ca72ff2..3976377b0c 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py @@ -5,18 +5,14 @@ from dataclasses import replace from typing import Any +import sqlbuild._native as _native from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile.classes.semantic_completion_inputs import SemanticCompletionInputs from sqlbuild.compiler.compile.models import CompiledProject, CompilerDiagnostic from sqlbuild.compiler.semantic_checks._helpers.completion import complete_native_diagnostics -from sqlbuild.compiler.semantic_checks._helpers.deferrals import record_semantic_deferral from sqlbuild.compiler.semantic_checks._helpers.metadata import native_metadata_diagnostics from sqlbuild.compiler.semantic_checks._helpers.payloads import session_fact_models from sqlbuild.compiler.semantic_checks._helpers.type_recovery import recover_native_output_types -from sqlbuild.compiler.semantic_checks.constants import ( - COMPLETION_DEFERRAL_SITE, - NATIVE_SEMANTIC_NO_CATALOG, -) +from sqlbuild.compiler.semantic_checks.constants import NATIVE_SEMANTIC_NO_CATALOG from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic @@ -27,33 +23,26 @@ def completed_semantic_project( binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], resource_sql_analysis: bool, session: Any | None, -) -> CompiledProject | None: - """Python's completed project, or None where any native step hands the stage back.""" +) -> CompiledProject: + """The completed project; a native internal failure raises `NativeCompilerError`.""" if project.binding_catalog is None: - record_semantic_deferral(kind=NATIVE_SEMANTIC_NO_CATALOG, site=COMPLETION_DEFERRAL_SITE) - return None + raise _native.NativeCompilerError(NATIVE_SEMANTIC_NO_CATALOG) catalog: object = project.binding_catalog.native session_models: frozenset[str] = session_fact_models(project=project, session=session) - recovered: CompiledProject | None = recover_native_output_types( + recovered: CompiledProject = recover_native_output_types( project=project, binding_results=binding_results, catalog=catalog, session=session, session_models=session_models, ) - if recovered is None: - return None - metadata: tuple[CompilerDiagnostic, ...] | None = native_metadata_diagnostics( + metadata: tuple[CompilerDiagnostic, ...] = native_metadata_diagnostics( project=recovered, profile=profile, resource_sql_analysis=resource_sql_analysis, catalog=catalog, ) - if metadata is None: - metadata = SemanticCompletionInputs.metadata_diagnostics( - project=recovered, profile=profile, resource_sql_analysis=resource_sql_analysis - ) return complete_native_diagnostics( project=replace(recovered, diagnostics=(*recovered.diagnostics, *metadata)), catalog=catalog, diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py b/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py index d433974d15..868a91e6b6 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py @@ -14,7 +14,6 @@ InferredColumn, ) from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.semantic_checks._helpers.deferrals import record_semantic_deferral from sqlbuild.compiler.semantic_checks._helpers.payloads import ( diagnostic_ids, has_blocking_binding, @@ -22,10 +21,7 @@ raw_binding_ids, ) from sqlbuild.compiler.semantic_checks.constants import ( - NATIVE_SEMANTIC_FAILURE, - NATIVE_TYPE_RECOVERY_DEFERRED, NATIVE_TYPE_RECOVERY_UNCHANGED, - TYPE_RECOVERY_DEFERRAL_SITE, UNKNOWN_RECOVERED_TYPE, ) from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations @@ -41,8 +37,8 @@ def recover_native_output_types( catalog: object, session: Any | None, session_models: frozenset[str], -) -> CompiledProject | None: - """Return `recover_output_types`'s project, or None where Python must recover the types.""" +) -> CompiledProject: + """The project with recovered output types; a native internal failure raises.""" if not has_blocking_binding(project): return project @@ -75,16 +71,10 @@ def recover_native_output_types( ) if recovery.status == NATIVE_TYPE_RECOVERY_UNCHANGED: return project - if recovery.status == NATIVE_TYPE_RECOVERY_DEFERRED: - record_semantic_deferral(kind=str(recovery.deferral), site=TYPE_RECOVERY_DEFERRAL_SITE) - return None poisoned: list[tuple[str, str]] = recovery.poisoned - outcome: tuple[list[tuple[int, str | None]], list[list[int]]] | None = recovery.finish( + outcome: tuple[list[tuple[int, str | None]], list[list[int]]] = recovery.finish( _revalidated(project=project, revalidated=recovery.revalidated, poisoned=poisoned) ) - if outcome is None: - record_semantic_deferral(kind=NATIVE_SEMANTIC_FAILURE, site=TYPE_RECOVERY_DEFERRAL_SITE) - return None kept, model_bindings = outcome diagnostics: tuple[CompilerDiagnostic, ...] = tuple( _with_note(diagnostic=project.diagnostics[index], note=note) for index, note in kept diff --git a/src/sqlbuild/compiler/semantic_checks/constants.py b/src/sqlbuild/compiler/semantic_checks/constants.py index 53d2036c0e..853738663f 100644 --- a/src/sqlbuild/compiler/semantic_checks/constants.py +++ b/src/sqlbuild/compiler/semantic_checks/constants.py @@ -1,14 +1,11 @@ -"""Native semantic completion statuses and deferral record sites.""" +"""Native semantic completion statuses and inputs.""" from sqlbuild.compiler.references.types import SqlReferenceKind NATIVE_TYPE_RECOVERY_UNCHANGED: str = "unchanged" -NATIVE_TYPE_RECOVERY_DEFERRED: str = "deferred" -NATIVE_SEMANTIC_NO_CATALOG: str = "no_analysis_catalog" -NATIVE_SEMANTIC_FAILURE: str = "native_failure" -TYPE_RECOVERY_DEFERRAL_SITE: str = "type_recovery.py" -COMPLETION_DEFERRAL_SITE: str = "recovery.py" -METADATA_DEFERRAL_SITE: str = "metadata_validation.py" +NATIVE_SEMANTIC_NO_CATALOG: str = ( + "NativeCompilerError: native semantic completion: the project has no analysis catalog" +) UNKNOWN_RECOVERED_TYPE: str = "UNKNOWN" METADATA_CONFIG_KEYS: tuple[str, ...] = ( "unique_key", diff --git a/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py b/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py index ca8f3e64ce..8ebffec6a9 100644 --- a/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py +++ b/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py @@ -17,8 +17,8 @@ def complete_native_semantic_diagnostics( binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], resource_sql_analysis: bool, session: Any | None = None, -) -> CompiledProject | None: - """Return the completed project, or None where Python must complete its diagnostics.""" +) -> CompiledProject: + """The completed project; a native internal failure raises `NativeCompilerError`.""" return completed_semantic_project( project=project, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/_test_types.py index 098f7b42a1..894ebd68e6 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/_test_types.py @@ -11,4 +11,3 @@ class NativeSemanticChecksCliTestCase: files: dict[str, str] expected_codes: tuple[str, ...] expected_notes: tuple[str, ...] - expected_native_deferrals: tuple[tuple[str, str], ...] diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/helpers.py index a1b15992d4..243d8eced2 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/helpers.py @@ -2,72 +2,39 @@ import json import subprocess -from collections import Counter from itertools import chain from pathlib import Path from typing import Any, NamedTuple, cast from sqlbuild.cli.compile_reuse.constants import REUSE_DISABLE_ENV_VAR -from sqlbuild.compiler.semantic_checks.constants import ( - COMPLETION_DEFERRAL_SITE, - METADATA_DEFERRAL_SITE, - TYPE_RECOVERY_DEFERRAL_SITE, -) -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.e2e.src.sqlbuild.cli.commands.main.compile.helpers import run_installed_sqb -SEMANTIC_WHEEL_SITES: tuple[str, ...] = ( - "compiler/compile/_helpers/diagnostics/type_recovery.py:_projection_spans", - "compiler/compile/_helpers/diagnostics/details.py:_parsed_model", - "compiler/compile/_helpers/assembly/metadata_validation.py:_function_errors", -) -SEMANTIC_DEFERRAL_SITES: frozenset[str] = frozenset( - {COMPLETION_DEFERRAL_SITE, METADATA_DEFERRAL_SITE, TYPE_RECOVERY_DEFERRAL_SITE} -) - class EngineSemanticRun(NamedTuple): - """One engine's compile report, semantic wheel calls and semantic-stage deferrals.""" + """One engine's compile report and exit code.""" report: dict[str, object] returncode: int - semantic_wheel_calls: int - deferrals: tuple[tuple[str, str], ...] def engine_semantic_run( *, project_dir: Path, files: dict[str, str], engine: str ) -> EngineSemanticRun: - """Write the project and compile it once under `engine` with analysis records on.""" + """Write the project and compile it once under `engine`.""" for relative_path, contents in files.items(): path: Path = project_dir / "project" / relative_path path.parent.mkdir(parents=True, exist_ok=True) _ = path.write_text(contents, encoding="utf-8") - record_dir: Path = project_dir / "records" compiled: subprocess.CompletedProcess[str] = run_installed_sqb( project_dir=project_dir / "project", args=("--compiler-engine", engine, "compile", "--json", "--no-cache"), - env={REUSE_DISABLE_ENV_VAR: "1", ANALYSIS_RECORD_DIR_ENV_VAR: str(record_dir)}, + env={REUSE_DISABLE_ENV_VAR: "1"}, ) report: dict[str, object] = json.loads(compiled.stdout.replace(str(project_dir), "")) _ = report.pop("compiler_engine", None) _ = report.pop("compile_timings", None) - calls: Counter[str] = Counter() - for path in record_dir.glob("polyglot-sites-*.json"): - for site, _, count in json.loads(path.read_text("utf-8"))["calls"]: - calls[site] += count - deferrals: list[tuple[str, str]] = [] - for path in sorted(record_dir.glob("analysis-deferrals-*.jsonl")): - for line in path.read_text("utf-8").splitlines(): - record: dict[str, str] = json.loads(line) - deferrals.append((record["kind"], record["site"])) - return EngineSemanticRun( - report=report, - returncode=compiled.returncode, - semantic_wheel_calls=sum(calls[site] for site in SEMANTIC_WHEEL_SITES), - deferrals=tuple(filter(lambda deferral: deferral[1] in SEMANTIC_DEFERRAL_SITES, deferrals)), - ) + return EngineSemanticRun(report=report, returncode=compiled.returncode) def diagnostic_codes(report: dict[str, object]) -> tuple[str, ...]: diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py index 80ccee60ec..e2b3b24729 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/semantic_checks/test_native_semantic_checks_cli.py @@ -103,14 +103,12 @@ "o.ordered_at is TIMESTAMP, 5 is INTEGER", "raw_orders has: amount, status, ordered_at, customer_id, order_id", ), - expected_native_deferrals=(), ), NativeSemanticChecksCliTestCase( description="UDF calls, config and cursor references, a source cursor and a SQL test", files=_METADATA_FILES, expected_codes=("B301", "B102", "B300", "B300", "B301", "B300", "B302"), expected_notes=(), - expected_native_deferrals=(), ), ], ids=lambda case: case.description, @@ -131,8 +129,6 @@ def test_given_failing_project_when_compiling_with_each_engine_then_diagnostics_ assert diagnostic_codes(native.report) == test_case.expected_codes assert set(test_case.expected_notes) <= diagnostic_notes(native.report) assert (preview.report, preview.returncode) == (native.report, native.returncode) - assert (native.semantic_wheel_calls, preview.semantic_wheel_calls) == (0, 0) - assert native.deferrals == preview.deferrals == test_case.expected_native_deferrals if __name__ == "__main__": diff --git a/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json b/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json new file mode 100644 index 0000000000..c1e0c0cba7 --- /dev/null +++ b/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json @@ -0,0 +1,42 @@ +[ +[ +"metadata", +{ +"None": "257eadf2980dd54d687bb376", +"duckdb": "d1d66a9dd0325471cc034afd", +"mysql": "257eadf2980dd54d687bb376", +"nonsense": "6f6079f31e12e028922d40f2", +"tsql": "257eadf2980dd54d687bb376" +} +], +[ +"metadata", +{ +"None": "c9f66e2829627aeb5e4728db", +"duckdb": "0ed0773a2a4a6fca23c1dcac", +"mysql": "c9f66e2829627aeb5e4728db", +"nonsense": "6f6079f31e12e028922d40f2", +"tsql": "c9f66e2829627aeb5e4728db" +} +], +[ +"metadata", +{ +"None": "a86fc9ef8fd7f46e7110dff4", +"duckdb": "30c78fd5afb3d05479d6c4ca", +"mysql": "a86fc9ef8fd7f46e7110dff4", +"nonsense": "6f6079f31e12e028922d40f2", +"tsql": "a86fc9ef8fd7f46e7110dff4" +} +], +[ +"metadata", +{ +"None": "9225fd40168382c4100e98c3", +"duckdb": "55da090c1795767af58252eb", +"mysql": "9225fd40168382c4100e98c3", +"nonsense": "6f6079f31e12e028922d40f2", +"tsql": "9225fd40168382c4100e98c3" +} +] +] diff --git a/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json b/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json new file mode 100644 index 0000000000..97b71711ec --- /dev/null +++ b/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json @@ -0,0 +1,74 @@ +[ +[ +"metadata", +{ +"bigquery": "3a936fb881b69845cbf281ea", +"duckdb": "3b771f6b2fe8c3590fa58424", +"postgres": "b0a7df99618a9706e15fffef", +"snowflake": "b0a7df99618a9706e15fffef" +} +], +[ +"metadata", +{ +"bigquery": "0a4ff65b0383da513b81728e", +"duckdb": "0347352e059171438345cdc3", +"postgres": "6fc977a6058882760800a845", +"snowflake": "6fc977a6058882760800a845" +} +], +[ +"metadata", +{ +"bigquery": "6188d55225fc3355ee350d93", +"duckdb": "7f34dcf40c6392f554ea91da", +"postgres": "2e203d99e28ab5cbdfd8cfb6", +"snowflake": "2e203d99e28ab5cbdfd8cfb6" +} +], +[ +"metadata", +{ +"bigquery": "c582c97bec0387c38abb4d4d", +"duckdb": "5248ff1ce3518aeed27d1ecd", +"postgres": "94e4aae33c04503253db41a4", +"snowflake": "94e4aae33c04503253db41a4" +} +], +[ +"metadata", +{ +"bigquery": "d1229780b7935d9acac4c6fa", +"duckdb": "f58eecbca144a88bbd6c7eef", +"postgres": "ad4229629c684797c3e9e363", +"snowflake": "ad4229629c684797c3e9e363" +} +], +[ +"metadata", +{ +"bigquery": "8a38d3db16cbefe4251c8244", +"duckdb": "d3057408147f96cf1aef3b61", +"postgres": "8bd1331b85712d5a5bed65b2", +"snowflake": "8bd1331b85712d5a5bed65b2" +} +], +[ +"metadata", +{ +"bigquery": "76c9a91c06ccf970b8cf8823", +"duckdb": "30c095b3a216c510c92743c5", +"postgres": "83535238d4bee99c5079d1f1", +"snowflake": "83535238d4bee99c5079d1f1" +} +], +[ +"metadata", +{ +"bigquery": "7b052937b7f3b212908fa289", +"duckdb": "31c36219fc8d6894d50fd2a6", +"postgres": "c6df91db98ed3db9d24d2dfc", +"snowflake": "c6df91db98ed3db9d24d2dfc" +} +] +] diff --git a/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json b/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json new file mode 100644 index 0000000000..64b357cbe9 --- /dev/null +++ b/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json @@ -0,0 +1,24 @@ +[ +[ +"completion", +{ +"databricks": "25140233ccf8e7760c91ffd6", +"mysql": "25140233ccf8e7760c91ffd6", +"oracle": "25140233ccf8e7760c91ffd6", +"sqlite": "25140233ccf8e7760c91ffd6", +"trino": "25140233ccf8e7760c91ffd6", +"tsql": "25140233ccf8e7760c91ffd6" +} +], +[ +"completion", +{ +"databricks": "edd3b1ec05f9481067078045", +"mysql": "edd3b1ec05f9481067078045", +"oracle": "edd3b1ec05f9481067078045", +"sqlite": "edd3b1ec05f9481067078045", +"trino": "edd3b1ec05f9481067078045", +"tsql": "edd3b1ec05f9481067078045" +} +] +] diff --git a/tests/goldens/python_oracle/semantic_formerly_deferred_no_dialect_and_an_unknown_dialect_raise_the_wheel_s_errors.json b/tests/goldens/python_oracle/semantic_formerly_deferred_no_dialect_and_an_unknown_dialect_raise_the_wheel_s_errors.json new file mode 100644 index 0000000000..8d1adcbf89 --- /dev/null +++ b/tests/goldens/python_oracle/semantic_formerly_deferred_no_dialect_and_an_unknown_dialect_raise_the_wheel_s_errors.json @@ -0,0 +1,16 @@ +[ +[ +"completion", +{ +"None": "8c4cd35551096786ad11558f", +"nonsense": "6f6079f31e12e028922d40f2" +} +], +[ +"completion", +{ +"None": "8c4cd35551096786ad11558f", +"nonsense": "6f6079f31e12e028922d40f2" +} +] +] diff --git a/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json b/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json new file mode 100644 index 0000000000..297a968cbf --- /dev/null +++ b/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json @@ -0,0 +1,38 @@ +[ +[ +"completion", +{ +"bigquery": "72198dc725abd42004ae2f92", +"duckdb": "72198dc725abd42004ae2f92", +"postgres": "72198dc725abd42004ae2f92", +"snowflake": "72198dc725abd42004ae2f92" +} +], +[ +"completion", +{ +"bigquery": "eb3b9bf0df6905ad935dadba", +"duckdb": "2bb0db315a6b4bd580d0f1da", +"postgres": "2bb0db315a6b4bd580d0f1da", +"snowflake": "2bb0db315a6b4bd580d0f1da" +} +], +[ +"completion", +{ +"bigquery": "167cc5c4a5563a44e435d35a", +"duckdb": "167cc5c4a5563a44e435d35a", +"postgres": "167cc5c4a5563a44e435d35a", +"snowflake": "167cc5c4a5563a44e435d35a" +} +], +[ +"completion", +{ +"bigquery": "b64b5864b0dd1efc66efe5c5", +"duckdb": "b64b5864b0dd1efc66efe5c5", +"postgres": "b64b5864b0dd1efc66efe5c5", +"snowflake": "b64b5864b0dd1efc66efe5c5" +} +] +] diff --git a/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json b/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json new file mode 100644 index 0000000000..b09f4e70a7 --- /dev/null +++ b/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json @@ -0,0 +1,74 @@ +[ +[ +"completion", +{ +"bigquery": "386e47117f671dcf1e99ba79", +"duckdb": "f8a96a6ad1b63bd08886df5e", +"postgres": "f8a96a6ad1b63bd08886df5e", +"snowflake": "f8a96a6ad1b63bd08886df5e" +} +], +[ +"completion", +{ +"bigquery": "6b2a976a568aea0841340272", +"duckdb": "6b2a976a568aea0841340272", +"postgres": "6b2a976a568aea0841340272", +"snowflake": "6b2a976a568aea0841340272" +} +], +[ +"completion", +{ +"bigquery": "da1919b4d28cc12b6aeb0d6a", +"duckdb": "da1919b4d28cc12b6aeb0d6a", +"postgres": "da1919b4d28cc12b6aeb0d6a", +"snowflake": "da1919b4d28cc12b6aeb0d6a" +} +], +[ +"completion", +{ +"bigquery": "0f8a7d8a92a2525f96180eab", +"duckdb": "0f8a7d8a92a2525f96180eab", +"postgres": "0f8a7d8a92a2525f96180eab", +"snowflake": "0f8a7d8a92a2525f96180eab" +} +], +[ +"completion", +{ +"bigquery": "a0775e9568c53c1c04d03b9e", +"duckdb": "a0775e9568c53c1c04d03b9e", +"postgres": "a0775e9568c53c1c04d03b9e", +"snowflake": "a0775e9568c53c1c04d03b9e" +} +], +[ +"completion", +{ +"bigquery": "56d1dd61d4d03302a8f2934a", +"duckdb": "ec5f01a5076273dcce4865e9", +"postgres": "ec5f01a5076273dcce4865e9", +"snowflake": "ec5f01a5076273dcce4865e9" +} +], +[ +"completion", +{ +"bigquery": "f9877a22ff91f0e41760f3cc", +"duckdb": "f9877a22ff91f0e41760f3cc", +"postgres": "f9877a22ff91f0e41760f3cc", +"snowflake": "f9877a22ff91f0e41760f3cc" +} +], +[ +"completion", +{ +"bigquery": "89c0ecb9bfa8db140822ca46", +"duckdb": "e8762d8e3f7d18a056f664ad", +"postgres": "e8762d8e3f7d18a056f664ad", +"snowflake": "e8762d8e3f7d18a056f664ad" +} +] +] diff --git a/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json b/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json new file mode 100644 index 0000000000..9000a6d33b --- /dev/null +++ b/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json @@ -0,0 +1,18 @@ +[ +[ +"completion", +"f85970edb78845ff2ce0ea2c" +], +[ +"completion", +"7149dab572362b7f40692734" +], +[ +"completion", +"c72855d94cb69680061f4edd" +], +[ +"completion", +"808ea581e0aab0f08630bc23" +] +] diff --git a/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json b/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json new file mode 100644 index 0000000000..292cd9e8ea --- /dev/null +++ b/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json @@ -0,0 +1,26 @@ +[ +[ +"completion", +"1b1aca58c29b7d2a4885cd24" +], +[ +"completion", +"7d957239333982bbc2a73159" +], +[ +"completion", +"17d9bf58e797f8ac09758305" +], +[ +"completion", +"758b2e20f1f61ae3237cc60b" +], +[ +"completion", +"d76482d15b26655a451ba2bc" +], +[ +"completion", +"28fd253e8477c9fa71e5c357" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/golden_views.py b/tests/integration/src/sqlbuild/compiler/golden_views.py index cb295310d1..e5596119d4 100644 --- a/tests/integration/src/sqlbuild/compiler/golden_views.py +++ b/tests/integration/src/sqlbuild/compiler/golden_views.py @@ -50,19 +50,22 @@ def canonical(value: object) -> object: return ["repr", repr(value)] -def digest(value: object) -> str: - """The SHA-256 of `value`'s canonical JSON, shortened for golden files.""" +def digest(value: object, *, masked: tuple[str, ...] = ()) -> str: + """The SHA-256 of `value`'s canonical JSON, with `masked` texts such as temporary paths + replaced, shortened for golden files.""" - encoded: bytes = json.dumps(canonical(value), sort_keys=True, ensure_ascii=False).encode() - return hashlib.sha256(encoded).hexdigest()[:24] + text: str = json.dumps(canonical(value), sort_keys=True, ensure_ascii=False) + for mask in masked: + text = text.replace(mask, "") + return hashlib.sha256(text.encode()).hexdigest()[:24] -def golden_entry(kind: str, view: object) -> GoldenEntry: +def golden_entry(kind: str, view: object, *, masked: tuple[str, ...] = ()) -> GoldenEntry: """One seam call's golden entry: a digest per named item for mappings, else one digest.""" if isinstance(view, dict): - return [kind, {str(name): digest(item) for name, item in view.items()}] - return [kind, digest(view)] + return [kind, {str(name): digest(item, masked=masked) for name, item in view.items()}] + return [kind, digest(view, masked=masked)] def golden_name(prefix: str, description: str) -> str: diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py index 10a8a6c3d3..eb81e271aa 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py @@ -5,7 +5,7 @@ @dataclass(frozen=True) class GeneratedSemanticParityTestCase: - """Seeded failing projects whose semantic completion Python and the native engine agree on.""" + """Seeded failing projects whose native completion matches the recorded Python outputs.""" description: str seed: int @@ -19,25 +19,36 @@ class GeneratedSemanticParityTestCase: @dataclass(frozen=True) -class DeferredSemanticTestCase: - """A project the native stage hands back to Python, which records the deferral.""" +class FormerlyDeferredSemanticTestCase: + """Projects the native stage once handed back to Python, now answered or raising natively.""" description: str - dialect: str - keeps_catalog: bool - non_ascii_comment: bool - expected_kinds: tuple[tuple[str, str], ...] + seed: int + count: int + non_ascii: bool + dialects: tuple[str | None, ...] + expected_outcomes: frozenset[str] + + +@dataclass(frozen=True) +class MissingCatalogTestCase: + """A project without the analysis catalog every compile builds.""" + + description: str + expected_message: str @dataclass(frozen=True) class GeneratedMetadataParityTestCase: - """Seeded projects whose metadata checks Python and the native engine must agree on.""" + """Seeded projects whose native metadata checks match the recorded Python outputs.""" description: str seed: int count: int model_count: int - dialects: tuple[str, ...] + dialects: tuple[str | None, ...] + non_ascii: bool + golden_prefix: str expected_minimum_native: int expected_minimum_families: dict[str, int] diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index d32f88ea10..1286925076 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -1,7 +1,7 @@ from __future__ import annotations -import json import random +import re from collections import Counter from collections.abc import Callable from dataclasses import dataclass, replace @@ -13,13 +13,9 @@ import sqlbuild._native as native_module import sqlbuild.compiler.compile._helpers.assembly.project as assembly_project -import sqlbuild.compiler.compile._helpers.diagnostics.recovery as recovery import sqlbuild.compiler.semantic_checks._helpers.stage as semantic_stage from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter -from sqlbuild.compiler.compile._helpers.assembly.metadata_validation import ( - get_semantic_metadata_diagnostics, -) from sqlbuild.compiler.compile._helpers.diagnostics.recovery import ( complete_semantic_diagnostics, ) @@ -68,12 +64,25 @@ type: DATE """ _OPT_OUT_SHARE: float = 0.2 +_NON_ASCII_NAMES: list[tuple[str, str]] = [ + ("amount", "am\u00f6unt"), + ("status", "\u017ftatus"), + ("customer_id", "customer_\u0130d"), + ("order_date", "order_d\u0101te"), + ("value", "va\u212alue"), + ("ordered_at", "ordered_\u0430t"), +] +_NON_ASCII_COMMENTS: tuple[str, ...] = ( + " /* caf\u00e9 \u00df \u2028 \u0131 */ ", + " -- na\u00efve\u0085x\n", + " /* \u0663\u0664 \u3000 */ ", + " ", +) _TYPO_SHARE: float = 0.45 _MIN_TYPO_LENGTH: int = 4 _ALIASES: tuple[tuple[str, str], ...] = (("", ""), ("o.", " AS o")) _SETTINGS: tuple[str, str] = ("", "\n[settings]\nrequire_sql_analysis = true\n") _OPT_OUTS: tuple[str, str] = ("", ", sql_analysis false") -_COMPLETION_STATUSES: tuple[str, str] = ("completion_native", "completion_deferred") _SOURCE_COMPARISON_SHARE: float = 0.6 type _Template = Callable[[random.Random, str, tuple[str, ...]], tuple[str, tuple[str, ...]]] @@ -208,7 +217,7 @@ def capture(**arguments: Any) -> CompiledProject: return captured[0] -def with_dialect(inputs: SemanticInputs, dialect: str) -> SemanticInputs: +def with_dialect(inputs: SemanticInputs, dialect: str | None) -> SemanticInputs: """The same stage inputs read under another SQL dialect.""" return replace( @@ -218,27 +227,10 @@ def with_dialect(inputs: SemanticInputs, dialect: str) -> SemanticInputs: ) -def python_completion( - *, inputs: SemanticInputs, monkeypatch: pytest.MonkeyPatch -) -> CompiledProject: - """The project Python completes when native semantic completion defers.""" - - with monkeypatch.context() as patch: - patch.setattr(recovery, "complete_native_semantic_diagnostics", lambda **_kwargs: None) - return complete_semantic_diagnostics( - project=inputs.project, - profile=inputs.profile, - binding_results=inputs.binding_results, - resource_sql_analysis=inputs.resource_sql_analysis, - ) - - def completed_natively(inputs: SemanticInputs) -> CompiledProject: - """The native stage's completed project, which must not defer.""" + """The native stage's completed project.""" - project: CompiledProject | None = native_completion(inputs) - assert project is not None - return project + return native_completion(inputs) def note_kinds(project: CompiledProject, kinds: tuple[str, ...]) -> Counter[str]: @@ -251,8 +243,8 @@ def note_kinds(project: CompiledProject, kinds: tuple[str, ...]) -> Counter[str] return counts -def native_completion(inputs: SemanticInputs) -> CompiledProject | None: - """The native stage's completed project, or None when it defers.""" +def native_completion(inputs: SemanticInputs) -> CompiledProject: + """The native stage's completed project.""" return complete_native_semantic_diagnostics( project=inputs.project, @@ -323,7 +315,7 @@ def _model_view(model: CompiledModel) -> tuple[object, ...]: def record_native_statuses(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count native type recovery plans and completed or deferred completions.""" + """Count native type recovery plans, completions and completed diagnostics.""" statuses: Counter[str] = Counter() plan: Callable[..., Any] = native_module.plan_semantic_type_recovery @@ -336,8 +328,8 @@ def counted_plan(*arguments: Any) -> Any: def counted_complete(*arguments: Any) -> Any: outcome: Any = complete(*arguments) - statuses[_COMPLETION_STATUSES[outcome[0] is not None]] += 1 - statuses["diagnostics_native"] += len(outcome[1]) + statuses["completion_native"] += 1 + statuses["diagnostics_native"] += len(outcome[0]) return outcome monkeypatch.setattr(native_module, "plan_semantic_type_recovery", counted_plan) @@ -345,15 +337,36 @@ def counted_complete(*arguments: Any) -> Any: return statuses -def deferral_records(record_dir: Path) -> tuple[tuple[str, str], ...]: - """Every `(kind, site)` deferral the native stage recorded below `record_dir`.""" +def non_ascii_variant(*, files: dict[str, str], rng: random.Random) -> dict[str, str]: + """The project with some column names spelt beyond ASCII and a comment after each model + header holding non-ASCII text, digits and Python-only line separators.""" + + renamed: list[tuple[str, str]] = rng.sample(_NON_ASCII_NAMES, k=rng.randint(1, 6)) + variant: dict[str, str] = {} + for path, text in files.items(): + for old, new in renamed: + text = re.sub(rf"\b{old}\b", new, text) + if path.endswith(".sql") and ";" in text: + header_end: int = text.index(";") + 1 + text = text[:header_end] + rng.choice(_NON_ASCII_COMMENTS) + text[header_end:] + variant[path] = text + return variant + + +def semantic_outcome(complete: Callable[[], object]) -> tuple[str, object]: + """`("completed", result)`, or the type and message of the error the stage raised.""" - records: list[tuple[str, str]] = [] - for path in sorted(record_dir.glob("analysis-deferrals-*.jsonl")): - for line in path.read_text("utf-8").splitlines(): - record: dict[str, str] = json.loads(line) - records.append((record["kind"], record["site"])) - return tuple(records) + try: + return ("completed", complete()) + except Exception as error: # noqa: BLE001 - the error is the outcome under comparison + return (type(error).__name__, str(error)) + + +def outcome_label(outcome: tuple[str, object]) -> str: + """`completed`, or the error as `Type: message`.""" + + kind, value = outcome + return kind if kind == "completed" else f"{kind}: {value}" _SOURCE_COLUMNS: tuple[str, ...] = ( @@ -507,18 +520,8 @@ def generated_metadata_files(*, rng: random.Random, model_count: int) -> dict[st return files -def python_metadata(*, inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...]: - """Python's metadata diagnostics, which native semantic completion defers to.""" - - return get_semantic_metadata_diagnostics( - project=inputs.project, - profile=inputs.profile, - resource_sql_analysis=inputs.resource_sql_analysis, - ) - - -def native_metadata(inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...] | None: - """The native stage's metadata diagnostics, or None when it defers.""" +def native_metadata(inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...]: + """The native stage's metadata diagnostics.""" catalog: Any = cast(Any, inputs.project.binding_catalog) return native_metadata_diagnostics( @@ -531,23 +534,23 @@ def native_metadata(inputs: SemanticInputs) -> tuple[CompilerDiagnostic, ...] | def _family_counts(outcome: Any) -> Counter[str]: counts: Counter[str] = Counter() - for function_rows, reference_rows in outcome[1]: + for function_rows, reference_rows in outcome[0]: counts.update(f"function {row[0]}" for row in function_rows) counts.update(f"reference {row[0]}" for row in reference_rows) - counts.update(f"source {row[0]}" for _, row in outcome[2]) - counts.update(f"sql_test {row[0]}" for _, row, _ in outcome[3]) + counts.update(f"source {row[0]}" for _, row in outcome[1]) + counts.update(f"sql_test {row[0]}" for _, row, _ in outcome[2]) return counts def record_metadata_families(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count native metadata runs, deferrals and errors by family and code.""" + """Count native metadata runs and errors by family and code.""" counts: Counter[str] = Counter() check: Callable[..., Any] = native_module.check_semantic_metadata_rows def counted(*arguments: Any) -> Any: outcome: Any = check(*arguments) - counts[("native", "deferred")[outcome[0] is not None]] += 1 + counts["native"] += 1 counts.update(_family_counts(outcome)) return outcome diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py index 676fedbd41..c724cbd2bc 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py @@ -1,14 +1,21 @@ -"""Native semantic metadata checks equal Python's on generated projects, family by family.""" +"""Native semantic metadata checks match the outputs recorded from Python's checks.""" from __future__ import annotations import random from collections import Counter +from functools import partial from pathlib import Path import pytest -from sqlbuild.compiler.compile.models import CompilerDiagnostic +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.semantic_checks._test_types import ( GeneratedMetadataParityTestCase, ) @@ -17,12 +24,18 @@ captured_semantic_inputs, generated_metadata_files, native_metadata, - python_metadata, + non_ascii_variant, record_metadata_families, + semantic_outcome, with_dialect, ) +def metadata_files(*, rng: random.Random, model_count: int, non_ascii: bool) -> dict[str, str]: + files: dict[str, str] = generated_metadata_files(rng=rng, model_count=model_count) + return non_ascii_variant(files=files, rng=rng) if non_ascii else files + + @pytest.mark.parametrize( "test_case", [ @@ -32,6 +45,8 @@ count=8, model_count=10, dialects=("duckdb", "postgres", "snowflake", "bigquery"), + non_ascii=False, + golden_prefix="metadata_generated", expected_minimum_native=32, expected_minimum_families={ "function B102": 100, @@ -41,32 +56,52 @@ "source B300": 4, "sql_test B302": 16, }, - ) + ), + GeneratedMetadataParityTestCase( + description="non-ASCII names and comments, new and unknown dialects", + seed=20261017, + count=4, + model_count=10, + dialects=("duckdb", "mysql", "tsql", None, "nonsense"), + non_ascii=True, + golden_prefix="metadata_formerly_deferred", + expected_minimum_native=12, + expected_minimum_families={"function B301": 10, "reference B300": 4}, + ), ], ids=lambda case: case.description, ) -def test_given_generated_projects_when_checking_metadata_natively_then_matches_python( +def test_given_generated_projects_when_checking_metadata_natively_then_matches_recorded_python( test_case: GeneratedMetadataParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: rng: random.Random = random.Random(test_case.seed) families: Counter[str] = record_metadata_families(monkeypatch=monkeypatch) - expected: list[tuple[CompilerDiagnostic, ...] | None] = [] - actual: list[tuple[CompilerDiagnostic, ...] | None] = [] + golden: list[GoldenEntry] = [] for index in range(test_case.count): + project_dir: Path = tmp_path / f"project_{index}" captured: SemanticInputs = captured_semantic_inputs( - project_dir=tmp_path / f"project_{index}", - files=generated_metadata_files(rng=rng, model_count=test_case.model_count), + project_dir=project_dir, + files=metadata_files( + rng=rng, model_count=test_case.model_count, non_ascii=test_case.non_ascii + ), monkeypatch=monkeypatch, ) - for dialect in test_case.dialects: - inputs: SemanticInputs = with_dialect(captured, dialect) - expected.append(python_metadata(inputs=inputs)) - actual.append(native_metadata(inputs)) + views: dict[str, object] = { + str(dialect): semantic_outcome( + partial(native_metadata, with_dialect(captured, dialect)) + ) + for dialect in test_case.dialects + } + golden.append(golden_entry("metadata", views, masked=(str(project_dir),))) - assert actual == expected - assert families["deferred"] == 0 + assert ( + golden_differences( + read_golden(golden_name(test_case.golden_prefix, test_case.description)), golden + ) + == [] + ) assert families["native"] >= test_case.expected_minimum_native assert { family: min(families[family], minimum) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index 6fce08d692..678d81990e 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -1,4 +1,4 @@ -"""Native semantic completion equals Python's on generated failing projects.""" +"""Native semantic completion matches the outputs recorded from Python's completion.""" from __future__ import annotations @@ -9,11 +9,19 @@ import pytest +import sqlbuild._native as native_module from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.semantic_checks._test_types import ( - DeferredSemanticTestCase, + FormerlyDeferredSemanticTestCase, GeneratedSemanticParityTestCase, + MissingCatalogTestCase, SessionCompletionTestCase, ) from tests.integration.src.sqlbuild.compiler.semantic_checks.helpers import ( @@ -21,14 +29,15 @@ captured_semantic_inputs, completed_natively, completion_view, - deferral_records, generated_semantic_files, native_completion, + non_ascii_variant, note_kinds, + outcome_label, proven_output_count, - python_completion, record_native_statuses, record_session_models, + semantic_outcome, session_corpus_files, with_dialect, without_session, @@ -62,7 +71,7 @@ ], ids=lambda case: case.description, ) -def test_given_generated_failing_projects_when_completing_natively_then_matches_python( +def test_given_generated_failing_projects_when_completing_natively_then_matches_recorded_python( test_case: GeneratedSemanticParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -70,30 +79,33 @@ def test_given_generated_failing_projects_when_completing_natively_then_matches_ rng: random.Random = random.Random(test_case.seed) statuses: Counter[str] = record_native_statuses(monkeypatch=monkeypatch) codes: Counter[str] = Counter() - expected_views: list[tuple[object, ...]] = [] - native_views: list[tuple[object, ...]] = [] + golden: list[GoldenEntry] = [] for index in range(test_case.count): + project_dir: Path = tmp_path / f"project_{index}" captured: SemanticInputs = captured_semantic_inputs( - project_dir=tmp_path / f"project_{index}", + project_dir=project_dir, files=generated_semantic_files( rng=rng, model_count=test_case.model_count, require_analysis=index % 2 == 0 ), monkeypatch=monkeypatch, ) + views: dict[str, object] = {} for dialect in test_case.dialects: - inputs: SemanticInputs = with_dialect(captured, dialect) - python: CompiledProject = python_completion(inputs=inputs, monkeypatch=monkeypatch) - codes.update(item.code for item in python.diagnostics) - codes.update(note_kinds(python, _NOTE_KINDS)) - expected_views.append(completion_view(python)) - native_views.append(completion_view(completed_natively(inputs))) - - assert native_views == expected_views - assert statuses["completion_deferred"] == 0 - assert statuses["type_recovery_deferred"] == 0 + native: CompiledProject = completed_natively(with_dialect(captured, dialect)) + codes.update(item.code for item in native.diagnostics) + codes.update(note_kinds(native, _NOTE_KINDS)) + views[dialect] = completion_view(native) + golden.append(golden_entry("completion", views, masked=(str(project_dir),))) + + assert ( + golden_differences( + read_golden(golden_name("semantic_generated", test_case.description)), golden + ) + == [] + ) + assert statuses["type_recovery_planned"] >= test_case.expected_minimum_type_recovery_plans assert statuses["completion_native"] >= test_case.expected_minimum_native_completions assert statuses["diagnostics_native"] >= test_case.expected_minimum_native_diagnostics - assert statuses["type_recovery_planned"] >= test_case.expected_minimum_type_recovery_plans assert { code: min(codes[code], minimum) for code, minimum in test_case.expected_minimum_codes.items() @@ -103,60 +115,105 @@ def test_given_generated_failing_projects_when_completing_natively_then_matches_ @pytest.mark.parametrize( "test_case", [ - DeferredSemanticTestCase( - description="a dialect outside the native parser build", - dialect="mysql", - keeps_catalog=True, - non_ascii_comment=False, - expected_kinds=(("unsupported_dialect", "type_recovery.py"),), + FormerlyDeferredSemanticTestCase( + description="non-ASCII names, comments and line separators", + seed=20261014, + count=4, + non_ascii=True, + dialects=("duckdb", "postgres", "snowflake", "bigquery"), + expected_outcomes=frozenset({"completed"}), ), - DeferredSemanticTestCase( - description="non-ASCII authored SQL in an explained model", - dialect="duckdb", - keeps_catalog=True, - non_ascii_comment=True, - expected_kinds=(("non_ascii_text", "recovery.py"),), + FormerlyDeferredSemanticTestCase( + description="dialects added to the native parser build", + seed=20261015, + count=2, + non_ascii=False, + dialects=("mysql", "tsql", "databricks", "sqlite", "oracle", "trino"), + expected_outcomes=frozenset({"completed"}), ), - DeferredSemanticTestCase( - description="a project without an analysis catalog to run on", - dialect="duckdb", - keeps_catalog=False, - non_ascii_comment=False, - expected_kinds=(("no_analysis_catalog", "recovery.py"),), + FormerlyDeferredSemanticTestCase( + description="no dialect and an unknown dialect raise the wheel's errors", + seed=20261016, + count=2, + non_ascii=True, + dialects=(None, "nonsense"), + expected_outcomes=frozenset( + { + "TypeError: argument 'dialect': 'None' is not an instance of 'str'", + "ValueError: Unknown dialect: nonsense", + } + ), ), ], ids=lambda case: case.description, ) -def test_given_deferred_projects_when_completing_then_python_completes_and_records( - test_case: DeferredSemanticTestCase, +def test_given_formerly_deferred_projects_when_completing_natively_then_matches_recorded_python( + test_case: FormerlyDeferredSemanticTestCase, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + rng: random.Random = random.Random(test_case.seed) + golden: list[GoldenEntry] = [] + outcomes: set[str] = set() + for index in range(test_case.count): + project_dir: Path = tmp_path / f"project_{index}" + files: dict[str, str] = generated_semantic_files( + rng=rng, model_count=12, require_analysis=index % 2 == 0 + ) + captured: SemanticInputs = captured_semantic_inputs( + project_dir=project_dir, + files=non_ascii_variant(files=files, rng=rng) if test_case.non_ascii else files, + monkeypatch=monkeypatch, + ) + views: dict[str, object] = {} + for dialect in test_case.dialects: + inputs: SemanticInputs = with_dialect(captured, dialect) + outcome: tuple[str, object] = semantic_outcome( + lambda inputs=inputs: completion_view(completed_natively(inputs)) + ) + outcomes.add(outcome_label(outcome)) + views[str(dialect)] = outcome + golden.append(golden_entry("completion", views, masked=(str(project_dir),))) + + assert ( + golden_differences( + read_golden(golden_name("semantic_formerly_deferred", test_case.description)), golden + ) + == [] + ) + assert outcomes == test_case.expected_outcomes + + +@pytest.mark.parametrize( + "test_case", + [ + MissingCatalogTestCase( + description="a project without an analysis catalog is an internal failure", + expected_message=( + "NativeCompilerError: native semantic completion: " + "the project has no analysis catalog" + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_project_without_catalog_when_completing_then_raises_native_compiler_error( + test_case: MissingCatalogTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - record_dir: Path = tmp_path / "records" captured: SemanticInputs = captured_semantic_inputs( project_dir=tmp_path / "project", files=generated_semantic_files(rng=random.Random(5), model_count=8, require_analysis=False), monkeypatch=monkeypatch, ) - inputs: SemanticInputs = with_dialect(captured, test_case.dialect) - project: CompiledProject = replace( - inputs.project, - binding_catalog=(None, inputs.project.binding_catalog)[test_case.keeps_catalog], - models=tuple( - replace( - model, - authored_sql=model.authored_sql - + ("", "-- caf\u00e9\n")[test_case.non_ascii_comment], - ) - for model in inputs.project.models - ), - ) - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) - native: CompiledProject | None = native_completion(replace(inputs, project=project)) + with pytest.raises(native_module.NativeCompilerError) as raised: + _ = native_completion( + replace(captured, project=replace(captured.project, binding_catalog=None)) + ) - assert native is None - assert deferral_records(record_dir) == test_case.expected_kinds + assert str(raised.value) == test_case.expected_message @pytest.mark.parametrize( @@ -206,22 +263,31 @@ def test_given_preview_compiles_when_completing_from_the_session_then_matches_pa ] selections: list[frozenset[str]] = record_session_models(monkeypatch=monkeypatch) codes: Counter[str] = Counter() - python_views: list[tuple[object, ...]] = [] + golden: list[GoldenEntry] = [] payload_views: list[tuple[object, ...]] = [] session_views: list[tuple[object, ...]] = [] - for inputs in captured: - python: CompiledProject = python_completion(inputs=inputs, monkeypatch=monkeypatch) - codes.update(item.code for item in python.diagnostics) - codes.update(note_kinds(python, _NOTE_KINDS)) - python_views.append(completion_view(python)) + for index, inputs in enumerate(captured): + completed: CompiledProject = completed_natively(inputs) + codes.update(item.code for item in completed.diagnostics) + codes.update(note_kinds(completed, _NOTE_KINDS)) + session_views.append(completion_view(completed)) payload_views.append(completion_view(completed_natively(without_session(inputs)))) - session_views.append(completion_view(completed_natively(inputs))) + golden.append( + golden_entry( + "completion", session_views[-1], masked=(str(tmp_path / f"project_{index}"),) + ) + ) session_models: int = sum(len(selected) for selected in selections) model_count: int = sum(len(inputs.project.models) for inputs in captured) assert all(inputs.native_session is not None for inputs in captured) - assert session_views == python_views - assert payload_views == python_views + assert ( + golden_differences( + read_golden(golden_name("semantic_session", test_case.description)), golden + ) + == [] + ) + assert payload_views == session_views assert ( session_models, model_count - session_models, From d2e3f192a2b94cade6e3e1c79e9afbe75e02e943 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:40:03 +0100 Subject: [PATCH 14/36] feat(native): infer every expression-source shape natively Untyped expression sources take Python's legacy analysis natively, running adapter nullability rules as the session does; a project restored without its catalog infers on a fresh one. Deletes Python's expression-source shape inference and the deferred-shapes fixture. --- .../main/expression_shapes.rs | 37 +++++++- .../src/assembly/analysis_session/models.rs | 5 +- .../tests/test_expression_shapes.rs | 9 ++ .../_helpers/analysis_session/session.rs | 39 +++++---- src/sqlbuild/_native.pyi | 6 +- .../_helpers/expression_shapes.py | 86 ++++++++----------- .../_helpers/model_analysis.py | 6 +- .../analysis_session/_helpers/profile_rows.py | 51 +++++++++++ .../analysis_session/_helpers/session_rows.py | 48 +---------- .../compiler/analysis_session/constants.py | 3 - .../_infer_native_expression_source_shapes.py | 6 +- .../_helpers/assembly/semantic_shapes.py | 75 ++-------------- .../_helpers/native_stages/assembly.py | 12 +-- tests/conftest.py | 8 -- .../analysis_session/_test_helpers.py | 2 +- .../compiler/analysis_session/helpers.py | 25 ++---- .../src/sqlbuild/compiler/lineage/helpers.py | 2 +- .../sqlbuild/compiler/pipeline/_test_types.py | 7 ++ .../pipeline/test_expression_catalog_cache.py | 83 ++++++++++++++---- 19 files changed, 261 insertions(+), 249 deletions(-) create mode 100644 src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs index 973018ff0f..764bf1ea64 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs @@ -1,9 +1,14 @@ //! Python's `get_expression_source_shapes` for expressions its catalog has not inferred. use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; +use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; + use crate::assembly::analysis_session::_helpers::compact_batch::{ Batch, BatchMember, MemberAnalysis, MemberResult, }; +use crate::assembly::analysis_session::_helpers::cte_facts::{ + LegacyAnalysis, LegacyInput, RecoveryProfile, legacy_analysis, +}; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; use crate::assembly::analysis_session::_helpers::publication::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::_helpers::session::shape_columns; @@ -47,7 +52,37 @@ pub fn expression_shapes( let mut shapes: Vec = Vec::with_capacity(results.len()); for (expression, result) in request.expressions.iter().zip(results) { shapes.push(match result.analysis { - MemberAnalysis::Legacy { .. } => ExpressionShape::Deferred, + MemberAnalysis::Legacy { .. } => { + let legacy: LegacyAnalysis = catch_compiler_panic(|| { + legacy_analysis(&LegacyInput { + cleaned_sql: &result.cleaned_sql, + lineage_references: &[], + recover: true, + types: &empty, + nullability: &empty, + profile: RecoveryProfile { + dialect: &request.dialect, + function_return_types: &request.function_return_types, + rules: request.nullability_rules.as_ref(), + callback: request.nullability_callback.as_ref(), + }, + }) + })?; + match legacy.columns { + Some(columns) if !columns.is_empty() && !legacy.has_star => { + ExpressionShape::Inferred(session_catalog.inferred_binding_shape( + &options, + ShapeSource { + sql: expression, + columns: shape_columns(&columns), + inputs: &no_inputs, + snapshot_columns: None, + }, + )?) + } + _ => ExpressionShape::Absent, + } + } MemberAnalysis::Projected { columns, has_star, .. } if !columns.is_empty() && !has_star => { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 6adceedf94..30873e3908 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -208,7 +208,6 @@ pub struct SessionOutcome { pub enum ExpressionShape { Inferred(Pairs), Absent, - Deferred, } /// What expression-source shape inference reads from the inference profile. @@ -217,6 +216,10 @@ pub struct ExpressionShapeRequest { pub dialect: String, pub case_sensitive_shapes: bool, pub function_return_types: Pairs, + /// Adapter nullability rules as `(function name, rule id)`. + pub nullability_rules: Option, + /// Runs the adapter's own rules, those with the `python` rule id. + pub nullability_callback: Option, pub expressions: Vec, } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_expression_shapes.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_expression_shapes.rs index 6811ffd806..cf037ef39a 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_expression_shapes.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_expression_shapes.rs @@ -48,12 +48,21 @@ fn given_source_expressions_when_inferring_shapes_then_matches_python_shapes() { expression: "SELECT 1 AS \"Mixed_Case\", CAST(2 AS BIGINT) AS total", expected_shape: r#"Inferred([("\"Mixed_Case\"", "INT"), ("total", "BIGINT")])"#, }, + ExpressionShapeTestCase { + description: "untyped projections take Python's legacy analysis natively", + dialect: "duckdb", + case_sensitive_shapes: false, + expression: "SELECT mystery_fn(1) AS event_id, NULL AS note", + expected_shape: r#"Inferred([("event_id", "UNKNOWN"), ("note", "UNKNOWN")])"#, + }, ]; for test_case in test_cases { let request = ExpressionShapeRequest { dialect: test_case.dialect.to_owned(), case_sensitive_shapes: test_case.case_sensitive_shapes, function_return_types: Vec::new(), + nullability_rules: Some(Vec::new()), + nullability_callback: None, expressions: vec![test_case.expression.to_owned()], }; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 3a9fbaf480..ac18a79c31 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -125,6 +125,7 @@ pub(crate) struct NativeModelAnalysisSession { /// What an internal native failure of the session names. const SESSION_CONTEXT: &str = "native model analysis"; +const SHAPES_CONTEXT: &str = "native expression-source shapes"; /// The native store kind holding finished model analyses. const MODEL_ANALYSIS_STORE_KIND: &str = "model-analyses"; @@ -266,33 +267,41 @@ fn start_model_analysis_session( }) } -/// Each expression's shape, with `False` where Python must infer it. +/// Each expression's shape, `None` where it has none; adapter rules run as in the session. #[pyfunction] +#[pyo3(signature = (catalog, request, adapter_rules=None))] fn infer_expression_source_shapes( py: Python<'_>, catalog: PyRef<'_, ProjectCatalog>, - request: (String, bool, Pairs, Vec), -) -> (Vec<(bool, Option)>, Option) { - let (dialect, case_sensitive_shapes, function_return_types, expressions) = request; + request: (String, bool, Pairs, Option, Vec), + adapter_rules: Option<(Py, Py)>, +) -> PyResult>> { + let (dialect, case_sensitive_shapes, function_return_types, nullability_rules, expressions) = + request; + let rule_failure: RuleFailure = RuleFailure::default(); let request = ExpressionShapeRequest { dialect, case_sensitive_shapes, function_return_types, + nullability_rules, + nullability_callback: adapter_rules.map(|(rules, nullability)| { + nullability_callback(rules, nullability, rule_failure.clone()) + }), expressions, }; let catalog = &catalog.inner; - match py.compiler_detach(|| expression_shapes(catalog, &request)) { - Ok(shapes) => (shapes.into_iter().map(expression_shape_row).collect(), None), - Err(error) => (Vec::new(), Some(error)), - } -} - -fn expression_shape_row(shape: ExpressionShape) -> (bool, Option) { - match shape { - ExpressionShape::Inferred(shape) => (true, Some(shape)), - ExpressionShape::Absent => (true, None), - ExpressionShape::Deferred => (false, None), + let shapes = py.compiler_detach(|| expression_shapes(catalog, &request)); + if let Some(error) = raised(&rule_failure) { + return Err(error); } + Ok(shapes + .map_err(|reason| compiler_error(native_failure(SHAPES_CONTEXT, &reason)))? + .into_iter() + .map(|shape| match shape { + ExpressionShape::Inferred(pairs) => Some(pairs), + ExpressionShape::Absent => None, + }) + .collect()) } fn session_request(request: RequestRow) -> SessionRequest { diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 60602133b9..1bd30af413 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -444,8 +444,10 @@ def prove_dynamic_column_contracts( /, ) -> list[_PivotContractRow]: ... def infer_expression_source_shapes( - catalog: object, request: tuple[str, bool, list[tuple[str, str]], list[str]], / -) -> tuple[list[tuple[bool, list[tuple[str, str]] | None]], str | None]: ... + catalog: object, + request: tuple[str, bool, list[tuple[str, str]], list[tuple[str, str]], list[str]], + adapter_rules: tuple[Mapping[str, Callable[..., object]], type] | None = None, +) -> list[list[tuple[str, str]] | None]: ... def _oracle_cte_fact_recovery( request: tuple[ str, diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/expression_shapes.py b/src/sqlbuild/compiler/analysis_session/_helpers/expression_shapes.py index 6a70d30bc3..1d2bb0c2aa 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/expression_shapes.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/expression_shapes.py @@ -2,71 +2,61 @@ from __future__ import annotations -import logging from typing import Any import sqlbuild._native as _native from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.adapter.contract.types import FunctionNullabilityRule from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral -from sqlbuild.compiler.analysis_session._helpers.session_rows import case_sensitive_shapes -from sqlbuild.compiler.analysis_session.constants import ( - DEFERRAL_EXPRESSION_SHAPES, - DEFERRAL_NO_CATALOG, - NATIVE_SHAPES_FAILURE_MESSAGE, +from sqlbuild.compiler.analysis_session._helpers.profile_rows import ( + adapter_nullability_rules, + case_sensitive_shapes, + nullability_rule_rows, ) +from sqlbuild.compiler.analysis_session.constants import ADAPTER_NULLABILITY_CALLBACK +from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.sql_analysis.constants import NATIVE_DIALECT_ALIASES -from sqlbuild.diagnostics.main.log_debug_event import log_debug_event - -_DEBUG_LOGGER: logging.Logger = logging.getLogger("sqlbuild.compile") +from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog def native_expression_source_shapes( *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile -) -> tuple[dict[str, str] | None, ...] | None: - """One shape per expression, or None (recorded) where Python must infer the shapes.""" +) -> tuple[dict[str, str] | None, ...]: + """One shape per expression; a native internal failure raises `NativeCompilerError`.""" - catalog: Any = profile.binding_catalog - if catalog is None: - record_analysis_deferral(kind=DEFERRAL_NO_CATALOG) - return None + dialect: str = profile.sql_analysis_dialect or "generic" + dialect = NATIVE_DIALECT_ALIASES.get(dialect, dialect) + catalog: Any = profile.binding_catalog or create_binding_catalog( + dialect=dialect, + quoted_ignore_case=profile.quoted_identifiers_ignore_case, + known_functions=(), + known_types=(), + relations={}, + ) cache: dict[str, dict[str, str] | None] = catalog.expression_shapes pending: tuple[str, ...] = tuple( dict.fromkeys(expression for expression in expressions if expression not in cache) ) - inferred: dict[str, dict[str, str] | None] | None = ( - _inferred_shapes(catalog=catalog, pending=pending, profile=profile) if pending else {} - ) - if inferred is None: - return None - cache.update(inferred) + if pending: + adapter_rules: dict[str, FunctionNullabilityRule] = adapter_nullability_rules(profile) + if adapter_rules: + record_analysis_deferral(kind=ADAPTER_NULLABILITY_CALLBACK) + shapes: list[list[tuple[str, str]] | None] = _native.infer_expression_source_shapes( + catalog.native, + ( + dialect, + case_sensitive_shapes(profile=profile, dialect=dialect), + list(profile.function_return_types.items()), + nullability_rule_rows(profile), + list(pending), + ), + (adapter_rules, InferredNullability) if adapter_rules else None, + ) + cache.update( + (expression, dict(shape) if shape is not None else None) + for expression, shape in zip(pending, shapes, strict=True) + ) return tuple( None if cache[expression] is None else dict(cache[expression] or {}) for expression in expressions ) - - -def _inferred_shapes( - *, catalog: Any, pending: tuple[str, ...], profile: ExpressionInferenceProfile -) -> dict[str, dict[str, str] | None] | None: - dialect: str = profile.sql_analysis_dialect or "generic" - dialect = NATIVE_DIALECT_ALIASES.get(dialect, dialect) - shapes, failure = _native.infer_expression_source_shapes( - catalog.native, - ( - dialect, - case_sensitive_shapes(profile=profile, dialect=dialect), - list(profile.function_return_types.items()), - list(pending), - ), - ) - deferred: int = len(pending) if failure is not None else sum(not done for done, _ in shapes) - if deferred: - record_analysis_deferral(kind=DEFERRAL_EXPRESSION_SHAPES, count=deferred) - log_debug_event( - logger=_DEBUG_LOGGER, message=NATIVE_SHAPES_FAILURE_MESSAGE, sqlbuild_error=failure - ) - return None - return { - expression: dict(shape) if shape is not None else None - for expression, (_, shape) in zip(pending, shapes, strict=True) - } diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py index 487b40c615..61782def29 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py @@ -7,10 +7,8 @@ import sqlbuild._native as _native from sqlbuild.adapter.contract.types import FunctionNullabilityRule from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral -from sqlbuild.compiler.analysis_session._helpers.session_rows import ( - adapter_nullability_rules, - session_request, -) +from sqlbuild.compiler.analysis_session._helpers.profile_rows import adapter_nullability_rules +from sqlbuild.compiler.analysis_session._helpers.session_rows import session_request from sqlbuild.compiler.analysis_session.classes.native_model_analysis import NativeModelAnalysis from sqlbuild.compiler.analysis_session.constants import ( ADAPTER_NULLABILITY_CALLBACK, diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py new file mode 100644 index 0000000000..45cbf00ac1 --- /dev/null +++ b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py @@ -0,0 +1,51 @@ +"""Inference-profile settings crossing into native analysis: rule ids and shape case.""" + +from __future__ import annotations + +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.adapter.contract.types import FunctionNullabilityRule +from sqlbuild.adapter.type_system.main._safe_cast_nullability import safe_cast_nullability +from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( + conditional_result_nullability, +) +from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability +from sqlbuild.compiler.analysis_session.constants import ( + NULLABILITY_RULE_ADAPTER, + NULLABILITY_RULE_CONDITIONAL_RESULT, + NULLABILITY_RULE_FIRST_ARG, + NULLABILITY_RULE_SAFE_CAST, +) +from sqlbuild.compiler.sql_analysis.constants import CASE_SENSITIVE_BINDING_DIALECTS + +_NULLABILITY_RULE_IDS: dict[FunctionNullabilityRule, str] = { + first_arg_nullability: NULLABILITY_RULE_FIRST_ARG, + conditional_result_nullability: NULLABILITY_RULE_CONDITIONAL_RESULT, + safe_cast_nullability: NULLABILITY_RULE_SAFE_CAST, +} + + +def nullability_rule_rows(profile: ExpressionInferenceProfile) -> list[tuple[str, str]]: + """Adapter nullability rules as `(name, rule id)`; an adapter's own rule is `python`.""" + + return [ + (name, _NULLABILITY_RULE_IDS.get(rule, NULLABILITY_RULE_ADAPTER)) + for name, rule in profile.function_nullability_rules.items() + ] + + +def adapter_nullability_rules( + profile: ExpressionInferenceProfile, +) -> dict[str, FunctionNullabilityRule]: + """The adapter's own nullability rules, which the native session calls back into.""" + + return { + name: rule + for name, rule in profile.function_nullability_rules.items() + if rule not in _NULLABILITY_RULE_IDS + } + + +def case_sensitive_shapes(*, profile: ExpressionInferenceProfile, dialect: str | None) -> bool: + """Python's `inferred_binding_shape` test for keeping authored identifier quoting.""" + + return not profile.quoted_identifiers_ignore_case and dialect in CASE_SENSITIVE_BINDING_DIALECTS diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 88afc03db3..827cc13352 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -5,17 +5,9 @@ from collections.abc import Mapping from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.adapter.contract.types import FunctionNullabilityRule -from sqlbuild.adapter.type_system.main._safe_cast_nullability import safe_cast_nullability -from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( - conditional_result_nullability, -) -from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability -from sqlbuild.compiler.analysis_session.constants import ( - NULLABILITY_RULE_ADAPTER, - NULLABILITY_RULE_CONDITIONAL_RESULT, - NULLABILITY_RULE_FIRST_ARG, - NULLABILITY_RULE_SAFE_CAST, +from sqlbuild.compiler.analysis_session._helpers.profile_rows import ( + case_sensitive_shapes, + nullability_rule_rows, ) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( @@ -36,16 +28,9 @@ InferredColumn, ) from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.sql_analysis.constants import CASE_SENSITIVE_BINDING_DIALECTS from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily -_NULLABILITY_RULE_IDS: dict[FunctionNullabilityRule, str] = { - first_arg_nullability: NULLABILITY_RULE_FIRST_ARG, - conditional_result_nullability: NULLABILITY_RULE_CONDITIONAL_RESULT, - safe_cast_nullability: NULLABILITY_RULE_SAFE_CAST, -} - def shape_rows(shapes: Mapping[str, Mapping[str, str]]) -> ShapeRows: """`{relation: {column: value}}` as ordered rows; discovery rejects non-text names and types.""" @@ -83,27 +68,6 @@ def session_request( ) -def nullability_rule_rows(profile: ExpressionInferenceProfile) -> list[tuple[str, str]]: - """Adapter nullability rules as `(name, rule id)`; an adapter's own rule is `python`.""" - - return [ - (name, _NULLABILITY_RULE_IDS.get(rule, NULLABILITY_RULE_ADAPTER)) - for name, rule in profile.function_nullability_rules.items() - ] - - -def adapter_nullability_rules( - profile: ExpressionInferenceProfile, -) -> dict[str, FunctionNullabilityRule]: - """The adapter's own nullability rules, which the native session calls back into.""" - - return { - name: rule - for name, rule in profile.function_nullability_rules.items() - if rule not in _NULLABILITY_RULE_IDS - } - - def contract_proof(row: ProofRow | None) -> DynamicColumnContractProof | None: """A native dynamic pivot proof as Python's, None when absent.""" @@ -123,12 +87,6 @@ def contract_proof(row: ProofRow | None) -> DynamicColumnContractProof | None: ) -def case_sensitive_shapes(*, profile: ExpressionInferenceProfile, dialect: str | None) -> bool: - """Python's `inferred_binding_shape` test for keeping authored identifier quoting.""" - - return not profile.quoted_identifiers_ignore_case and dialect in CASE_SENSITIVE_BINDING_DIALECTS - - def column_rows(columns: tuple[InferredColumn, ...] | None) -> list[ColumnRow] | None: if columns is None: return None diff --git a/src/sqlbuild/compiler/analysis_session/constants.py b/src/sqlbuild/compiler/analysis_session/constants.py index 5685c3adf9..769d729c97 100644 --- a/src/sqlbuild/compiler/analysis_session/constants.py +++ b/src/sqlbuild/compiler/analysis_session/constants.py @@ -1,12 +1,9 @@ """Native model analysis session constants.""" ANALYSIS_DEFERRAL_SITE: str = "analysis_session" -DEFERRAL_NO_CATALOG: str = "no_analysis_catalog" -DEFERRAL_EXPRESSION_SHAPES: str = "expression_shapes" LINEAGE_NATIVE: str = "native" LINEAGE_FACTS: str = "facts" NATIVE_ANALYSIS_FAILURE_MESSAGE: str = "native compact query analysis failed" -NATIVE_SHAPES_FAILURE_MESSAGE: str = "native expression-source shape inference failed; falling back" NULLABILITY_RULE_FIRST_ARG: str = "first_arg" NULLABILITY_RULE_CONDITIONAL_RESULT: str = "conditional_result" NULLABILITY_RULE_SAFE_CAST: str = "safe_cast" diff --git a/src/sqlbuild/compiler/analysis_session/main/_infer_native_expression_source_shapes.py b/src/sqlbuild/compiler/analysis_session/main/_infer_native_expression_source_shapes.py index 53cb602fc9..7509f3c4f5 100644 --- a/src/sqlbuild/compiler/analysis_session/main/_infer_native_expression_source_shapes.py +++ b/src/sqlbuild/compiler/analysis_session/main/_infer_native_expression_source_shapes.py @@ -1,4 +1,4 @@ -"""Infer expression-source shapes natively for the preview compiler engine.""" +"""Infer expression-source shapes natively.""" from __future__ import annotations @@ -10,7 +10,7 @@ def infer_native_expression_source_shapes( *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile -) -> tuple[dict[str, str] | None, ...] | None: - """Return one shape per expression, or None where Python must infer the shapes.""" +) -> tuple[dict[str, str] | None, ...]: + """One shape per expression; a native internal failure raises `NativeCompilerError`.""" return native_expression_source_shapes(expressions=expressions, profile=profile) diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py b/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py index 0513354e15..77d79afe5c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py @@ -3,15 +3,13 @@ from __future__ import annotations from dataclasses import replace -from typing import Any, cast +from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.columns import table_function_analysis_name -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - NativeCompactAnalysis, - analyze_columns_and_lineage_with_polyglot, - analyze_queries_with_compact_polyglot_batch, +from sqlbuild.compiler.analysis_session.main._infer_native_expression_source_shapes import ( + infer_native_expression_source_shapes, ) +from sqlbuild.compiler.compile._helpers.analysis.columns import table_function_analysis_name from sqlbuild.compiler.compile.constants import NOT_NULL_AUDIT_NAME from sqlbuild.compiler.compile.models import ( CompiledModel, @@ -19,7 +17,6 @@ CompileModelInput, CompileProjectInputs, CompileSqlReference, - PolyglotAnalysisResult, ) from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.planner.constants import ( @@ -30,7 +27,6 @@ from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.constants import ( CASE_SENSITIVE_BINDING_DIALECTS, - NATIVE_DIALECT_ALIASES, ) from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql @@ -258,7 +254,7 @@ def semantic_shapes( } elif entry.expression: expression_sources.append((source.name, entry.expression)) - source_shapes: tuple[dict[str, str] | None, ...] = get_expression_source_shapes( + source_shapes: tuple[dict[str, str] | None, ...] = infer_native_expression_source_shapes( expressions=tuple(expression for _, expression in expression_sources), profile=profile ) for (source_name, _), source_shape in zip(expression_sources, source_shapes, strict=True): @@ -316,67 +312,6 @@ def semantic_shapes( return shapes -def get_expression_source_shapes( - *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile | None -) -> tuple[dict[str, str] | None, ...]: - """Infer expression-source shapes in one native batch, reusing the compile catalog's answers.""" - effective_profile: ExpressionInferenceProfile = profile or ExpressionInferenceProfile() - effective_profile = replace( - effective_profile, - sql_analysis_dialect=NATIVE_DIALECT_ALIASES.get( - effective_profile.sql_analysis_dialect or "generic", - effective_profile.sql_analysis_dialect, - ), - ) - catalog: Any = effective_profile.binding_catalog - cache: dict[str, dict[str, str] | None] = ( - catalog.expression_shapes if catalog is not None else {} - ) - pending: tuple[str, ...] = tuple( - dict.fromkeys(expression for expression in expressions if expression not in cache) - ) - if pending: - prepared: tuple[NativeCompactAnalysis, ...] = analyze_queries_with_compact_polyglot_batch( - query_sqls=pending, - references=tuple(() for _ in pending), - placeholders=tuple(None for _ in pending), - column_nullability_by_table={}, - column_types_by_table={}, - inference_profile=effective_profile, - recover_cte_facts=tuple(True for _ in pending), - rich_type_inference=True, - ) - for expression, precomputed in zip(pending, prepared, strict=True): - cache[expression] = _expression_source_shape( - expression=expression, profile=effective_profile, precomputed=precomputed - ) - return tuple( - None if cache[expression] is None else dict(cast(dict[str, str], cache[expression])) - for expression in expressions - ) - - -def _expression_source_shape( - *, expression: str, profile: ExpressionInferenceProfile, precomputed: NativeCompactAnalysis -) -> dict[str, str] | None: - analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=expression, - references=(), - inference_profile=profile, - allow_compact_analysis=True, - recover_cte_facts=True, - precomputed=precomputed, - ) - if not analysis.columns or analysis.has_star: - return None - return inferred_binding_shape( - sql=expression, - profile=profile, - columns={column.name: column.type or "UNKNOWN" for column in analysis.columns}, - inputs={}, - ) - - def referenced_model_names( *, model_input: CompileModelInput, available_names: frozenset[str] | None = None ) -> tuple[str, ...]: diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 76e91b709a..3224c5b85c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -21,9 +21,6 @@ ) from sqlbuild.compiler.compile._helpers.analysis.pivot_requests import standalone_pivot_models from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_syntax_checks -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( - get_expression_source_shapes, -) from sqlbuild.compiler.compile.models import ( CompileProjectInputs, DynamicColumnContractProof, @@ -108,14 +105,9 @@ def project_facts_by_engine( def expression_source_shapes_by_engine( *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile ) -> tuple[dict[str, str] | None, ...]: - """Infer one shape per expression source natively, or with Python where native defers.""" + """Infer one shape per expression source natively.""" - native_shapes: tuple[dict[str, str] | None, ...] | None = infer_native_expression_source_shapes( - expressions=expressions, profile=profile - ) - if native_shapes is not None: - return native_shapes - return get_expression_source_shapes(expressions=expressions, profile=profile) + return infer_native_expression_source_shapes(expressions=expressions, profile=profile) def analyze_model_sql( diff --git a/tests/conftest.py b/tests/conftest.py index d62b6a27d3..2427d418f2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,6 @@ import pytest from sqlbuild.cli.compile_reuse.constants import REUSE_DISABLE_ENV_VAR, REUSE_DISABLE_VALUE -from sqlbuild.compiler.compile._helpers.native_stages import assembly as native_assembly def pytest_configure(config: pytest.Config) -> None: @@ -15,10 +14,3 @@ def pytest_configure(config: pytest.Config) -> None: del config os.environ.setdefault(REUSE_DISABLE_ENV_VAR, REUSE_DISABLE_VALUE) - - -@pytest.fixture -def deferred_native_expression_shapes(monkeypatch: pytest.MonkeyPatch) -> None: - """Make native expression-source shapes defer, so compiles run the Python shape inference.""" - - monkeypatch.setattr(native_assembly, "infer_native_expression_source_shapes", lambda **_: None) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py index 7622a8d2bf..ec47794d45 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py @@ -22,7 +22,7 @@ from sqlbuild.adapters.postgres.classes.postgres_adapter import PostgresAdapter from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter from sqlbuild.adapters.sqlserver.classes.sqlserver_adapter import SqlServerAdapter -from sqlbuild.compiler.analysis_session._helpers.session_rows import nullability_rule_rows +from sqlbuild.compiler.analysis_session._helpers.profile_rows import nullability_rule_rows from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( _polyglot_cte_passthrough_facts, _polyglot_filtered_non_null_outputs, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 0aac71deab..efa50f07a8 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -39,9 +39,6 @@ model_pivot_sql, ) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_placeholders -from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( - get_expression_source_shapes, -) from sqlbuild.compiler.compile._helpers.render.cursor_intrinsics import ( cursor_intrinsics_analysis_sql, ) @@ -629,26 +626,16 @@ def analysed_three_ways( _compare_proofs(request=request, analyses=analyses, session=session, runs=runs) return analyses, session - def shapes_by_both( + def recorded_shapes( *, expressions: tuple[str, ...], profile: ExpressionInferenceProfile ) -> tuple[dict[str, str] | None, ...]: catalog: Any = cast(Any, profile.binding_catalog) - native_catalog: Any = catalog.with_relations({}) - native: tuple[dict[str, str] | None, ...] | None = infer_native_expression_source_shapes( - expressions=expressions, profile=replace(profile, binding_catalog=native_catalog) - ) - python: tuple[dict[str, str] | None, ...] = get_expression_source_shapes( + native: tuple[dict[str, str] | None, ...] = infer_native_expression_source_shapes( expressions=expressions, profile=profile ) - runs.expression_shapes += sum(shape is not None for shape in python) - runs.golden.append(golden_entry("shapes", (native, native_catalog.expression_shapes))) - _append( - runs, - "expression shapes", - (python, catalog.expression_shapes), - (native, native_catalog.expression_shapes), - ) - return python + runs.expression_shapes += sum(shape is not None for shape in native) + runs.golden.append(golden_entry("shapes", (native, catalog.expression_shapes))) + return native native_lineage_facts: Callable[..., tuple[CompiledLineageColumnFact, ...]] = ( native_model_analysis.lineage_facts @@ -669,7 +656,7 @@ def recorded_model(*arguments: Any, **keywords: Any) -> CompiledModel: patch.setattr(native_model_analysis, "lineage_facts", counted_native_enrichment) patch.setattr(project_assembly, "_assemble_compiled_model", recorded_model) patch.setattr(project_assembly, "analyze_model_sql", analysed_three_ways) - patch.setattr(project_assembly, "expression_source_shapes_by_engine", shapes_by_both) + patch.setattr(project_assembly, "expression_source_shapes_by_engine", recorded_shapes) with suppress(CompileInputError): _ = project_assembly.assemble_compiled_project( inputs=inputs, diff --git a/tests/integration/src/sqlbuild/compiler/lineage/helpers.py b/tests/integration/src/sqlbuild/compiler/lineage/helpers.py index 913cdb4b31..bcc175b0bd 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/helpers.py @@ -209,7 +209,7 @@ def generated_lineage_files(*, rng: random.Random, model_count: int) -> dict[str def compiled_project(*, project_dir: Path, files: dict[str, str]) -> CompiledProject: - """Write and compile a project with the Python compiler, keeping any diagnostics.""" + """Write and compile a project, keeping any diagnostics.""" for relative_path, contents in files.items(): path: Path = project_dir / relative_path diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py index 958b0b3d96..5a13a6279a 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py @@ -13,6 +13,13 @@ class ExpressionMemoCase: expected_analysis_calls: int = 1 +@dataclass(frozen=True) +class RestoredShapeCase: + description: str + expressions: dict[str, str] + expected_shapes: dict[str, dict[str, str]] + + @dataclass(frozen=True) class ExpressionBatchCase: description: str diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py index 52aeb7871c..e802ce57fd 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py @@ -2,19 +2,36 @@ import json from collections.abc import Callable +from dataclasses import replace from pathlib import Path from typing import Any import pytest +import sqlbuild._native as native_module from sqlbuild.cli.commands.main.entrypoint.entry import main -from sqlbuild.compiler.compile._helpers.assembly import semantic_shapes +from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes +from sqlbuild.compiler.compile.models import CompiledProject +from tests.integration.src.sqlbuild.compiler.lineage.helpers import compiled_project from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( ExpressionBatchCase, ExpressionMemoCase, + RestoredShapeCase, ) -pytestmark: pytest.MarkDecorator = pytest.mark.usefixtures("deferred_native_expression_shapes") + +def traced_shape_batches(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, ...]]: + """Record the expressions of every native expression-source shape batch.""" + + original: Callable[..., Any] = native_module.infer_expression_source_shapes + batches: list[tuple[str, ...]] = [] + + def traced(catalog: object, request: tuple[Any, ...], *rules: Any) -> Any: + batches.append(tuple(request[4])) + return original(catalog, request, *rules) + + monkeypatch.setattr(native_module, "infer_expression_source_shapes", traced) + return batches @pytest.mark.parametrize( @@ -40,14 +57,7 @@ def test_given_equal_source_expressions_when_compiling_then_infers_once_and_inva source.write_text( f"sources:\n - name: raw_orders\n description: Test source raw_orders.\n expression: {test_case.expression}\n - name: raw_customers\n description: Test source raw_customers.\n expression: {test_case.expression}\n" ) - original: Callable[..., Any] = semantic_shapes.analyze_queries_with_compact_polyglot_batch - calls: list[object] = [] - - def traced(**kwargs: Any) -> Any: - calls.append(kwargs["query_sqls"]) - return original(**kwargs) - - monkeypatch.setattr(semantic_shapes, "analyze_queries_with_compact_polyglot_batch", traced) + calls: list[tuple[str, ...]] = traced_shape_batches(monkeypatch) args: list[str] = ["--project-dir", str(tmp_path), "compile", "--json"] assert main(args) == 0 payload: dict[str, Any] = json.loads(capsys.readouterr().out) @@ -91,19 +101,56 @@ def test_given_distinct_source_expressions_when_compiling_then_infers_them_in_on f" - name: raw_orders\n description: Test source raw_orders.\n expression: {test_case.orders_expression}\n" f" - name: raw_customers\n description: Test source raw_customers.\n expression: {test_case.customers_expression}\n" ) - original: Callable[..., Any] = semantic_shapes.analyze_queries_with_compact_polyglot_batch - batches: list[tuple[str, ...]] = [] - - def traced(**kwargs: Any) -> Any: - batches.append(kwargs["query_sqls"]) - return original(**kwargs) - - monkeypatch.setattr(semantic_shapes, "analyze_queries_with_compact_polyglot_batch", traced) + batches: list[tuple[str, ...]] = traced_shape_batches(monkeypatch) assert main(["--project-dir", str(tmp_path), "compile", "--json"]) == 0 payload: dict[str, Any] = json.loads(capsys.readouterr().out) assert payload["diagnostics"] == [] assert tuple(batches) == test_case.expected_batches +@pytest.mark.parametrize( + "test_case", + [ + RestoredShapeCase( + description="closed and open expression sources", + expressions={ + "raw_orders": "SELECT 1 AS id, 'placed' AS status", + "raw_events": "SELECT mystery_fn(1) AS event_id, NULL AS note", + "raw_open": "SELECT * FROM somewhere", + }, + expected_shapes={ + "raw_orders": {"id": "INT", "status": "TEXT"}, + "raw_events": {"event_id": "UNKNOWN", "note": "UNKNOWN"}, + }, + ) + ], + ids=lambda case: case.description, +) +def test_given_restored_project_without_catalog_when_reading_shapes_then_infers_natively( + test_case: RestoredShapeCase, tmp_path: Path +) -> None: + sources: str = "sources:\n" + "".join( + f" - name: {name}\n description: Test source {name}.\n expression: {expression}\n" + for name, expression in test_case.expressions.items() + ) + project: CompiledProject = compiled_project( + project_dir=tmp_path, + files={ + "sqlbuild_project.toml": 'name = "orders"\nadapter = "duckdb"\n', + "sources/sources.yml": sources, + }, + ) + + restored: dict[str, dict[str, str]] = semantic_shapes( + project=replace(project, binding_catalog=None) + ) + + assert {name: restored.get(name) for name in test_case.expected_shapes} == ( + test_case.expected_shapes + ) + assert "raw_open" not in restored + assert restored == semantic_shapes(project=project) + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From 3bd3a866912d3f6f4ca804e1e68f103fd31eb25b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:10:38 +0100 Subject: [PATCH 15/36] feat(native)!: infer columns outside the analysis session natively Models native analysis cannot project (CREATE TABLE AS, INSERT ... SELECT, deeply nested calls) keep the columns Python's parsed-tree inference gave them; table-function bodies, SQL-test fixture interfaces, source rebinding and planner fixture inference run the matching native analysis. A model the session leaves unanalysed is a broken invariant and raises NativeCompilerError. Deletes Python's compact, parsed-tree and CTE fact analyses; the few reference and placeholder helpers left move to reference_names, binding_requests and cte_scopes. The CTE fact parity test matches facts recorded from Python's recovery. --- .../analysis_session/_helpers/cte_facts.rs | 5 +- .../analysis_session/_helpers/enrichment.rs | 1 + .../analysis_session/_helpers/session.rs | 1 + .../main/expression_shapes.rs | 1 + .../src/assembly/analysis_session/main/mod.rs | 1 + .../analysis_session/main/query_columns.rs | 180 + .../src/assembly/analysis_session/models.rs | 52 +- .../analysis_session/tests/helpers.rs | 1 + .../src/assembly/project/_helpers/syntax.rs | 2 +- .../bindings/_helpers/analysis_session/mod.rs | 1 + .../analysis_session/query_columns.rs | 110 + .../_helpers/analysis_session/registration.rs | 3 +- src/sqlbuild/_native.pyi | 12 + .../_helpers/dynamic_contracts.py | 7 +- .../analysis_session/_helpers/profile_rows.py | 12 + .../_helpers/query_columns.py | 120 + .../analysis_session/_helpers/session_rows.py | 10 +- .../main/_infer_native_query_columns.py | 27 + .../compiler/analysis_session/models.py | 23 + .../compiler/analysis_session/types.py | 4 + .../_helpers/analysis/binding_requests.py | 33 + .../compile/_helpers/analysis/columns.py | 1155 - .../compile/_helpers/analysis/compact.py | 1834 -- .../compile/_helpers/analysis/cte_facts.py | 1196 - .../compile/_helpers/analysis/cte_scopes.py | 67 + .../_helpers/analysis/fixture_columns.py | 16 +- .../_helpers/analysis/pivot_requests.py | 4 +- .../_helpers/analysis/reference_names.py | 131 + .../_helpers/analysis/required_columns.py | 12 +- .../_helpers/assembly/metadata_validation.py | 35 +- .../compile/_helpers/assembly/project.py | 45 +- .../_helpers/assembly/semantic_shapes.py | 2 +- .../_helpers/assembly/source_bindings.py | 27 +- .../compile/_helpers/attachment/functions.py | 13 +- .../compile/_helpers/sharing/binding.py | 2 +- .../compile/classes/python_model_analysis.py | 2 +- .../classes/semantic_completion_inputs.py | 4 +- .../compiler/sql_analysis/constants.py | 2 + ...es_set_operations_stars_joins_filters.json | 20002 ++++++++++++++++ .../src/sqlbuild/cli/commands/main/helpers.py | 14 +- .../analysis_session/_test_helpers.py | 64 +- .../compiler/analysis_session/_test_types.py | 27 + .../compiler/analysis_session/helpers.py | 19 +- .../test_native_cte_fact_recovery.py | 21 +- .../test_native_model_analysis.py | 5 +- .../test_native_query_columns.py | 142 + .../src/sqlbuild/compiler/pipeline/helpers.py | 5 - .../compile/_helpers/analysis/_test_types.py | 8 - .../_helpers/analysis/test_compact_workers.py | 58 - .../_helpers/render/test_cursor_intrinsics.py | 11 +- .../_helpers/test_sql_analysis_columns.py | 1296 +- 51 files changed, 21127 insertions(+), 5698 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs create mode 100644 src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py create mode 100644 src/sqlbuild/compiler/analysis_session/main/_infer_native_query_columns.py create mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/binding_requests.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/columns.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/compact.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/cte_facts.py create mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/cte_scopes.py create mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py create mode 100644 tests/goldens/python_oracle/cte_facts_casts_case_coalesce_if_iff_adapter_rules_set_operations_stars_joins_filters.json create mode 100644 tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py delete mode 100644 tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_compact_workers.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs index d7b018812a..fa40ab5478 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs @@ -108,6 +108,9 @@ pub(crate) struct LegacyInput<'a> { /// Python's `lineage_reference_map` items. pub(crate) lineage_references: &'a [(String, String, String)], pub(crate) recover: bool, + /// Python's `_infer_columns_from_polyglot_ast`, which infers nullability through input + /// columns even where no input nullability is known; lineage analysis stays shallow there. + pub(crate) full_nullability: bool, /// Python's `column_types_by_table`: every known relation's types. pub(crate) types: &'a ShapeTable, /// Python's `column_nullability_by_table`, in its dict order. @@ -197,7 +200,7 @@ pub(crate) fn legacy_analysis(input: &LegacyInput<'_>) -> Fact { NON_NULL_NULLABILITY } else if let Some(cte_nullability) = dict_get(&cte_nullability, output) { cte_nullability - } else if known { + } else if known || input.full_nullability { context.nullability(inner, &aliases, &nullability)? } else { context.shallow_nullability(inner)? diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index 038c4c0ca6..3f5eefe01d 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -108,6 +108,7 @@ fn legacy(input: &EnrichmentInput<'_>, cleaned: &str) -> Result, + request: &QueryColumnsRequest, +) -> Result, String> { + let types: ShapeTable = ShapeTable::from_shapes(&request.types); + let nullability: ShapeTable = ShapeTable::from_shapes(&request.nullability); + let analysed: Vec<(usize, &QueryColumnsInput)> = request + .queries + .iter() + .enumerate() + .filter(|(_, query)| query.mode == QueryColumnsMode::Batch) + .collect(); + let mut batch_results: HashMap = HashMap::new(); + if !analysed.is_empty() { + let catalog: &ProjectCatalog = + catalog.ok_or("a compact query batch without an analysis catalog")?; + let mut session_catalog: SessionCatalog = SessionCatalog::new(catalog, &Vec::new()); + let batch = Batch { + dialect: &request.dialect, + function_return_types: &request.function_return_types, + rich_type_inference: true, + members: analysed + .iter() + .map(|(_, query)| BatchMember { + query_sql: &query.sql, + placeholders: &query.placeholders, + analysis_names: query.analysis_names.iter().map(String::as_str).collect(), + lineage_references: &query.lineage_references, + recover_cte_facts: query.recover_cte_facts, + binding_schema: None, + }) + .collect(), + types: &types, + nullability: &nullability, + }; + let results: Vec = session_catalog.analyze_batch(&batch)?; + batch_results.extend(analysed.iter().map(|(index, _)| *index).zip(results)); + } + let tables = Tables { + request, + types: &types, + nullability: &nullability, + }; + request + .queries + .iter() + .enumerate() + .map(|(index, query)| match batch_results.remove(&index) { + None if query.mode == QueryColumnsMode::Reanalysis => tables.reanalysed(query), + None => tables.parsed( + query, + &parse_cleaned_sql(query), + query.mode == QueryColumnsMode::Parse, + ), + Some(result) => match result.analysis { + MemberAnalysis::Projected { + columns, has_star, .. + } => Ok(QueryColumns { + succeeded: true, + columns: Some(columns), + has_star, + }), + MemberAnalysis::Legacy { .. } => tables.parsed(query, &result.cleaned_sql, false), + MemberAnalysis::Failed => Ok(QueryColumns::default()), + }, + }) + .collect() +} + +/// Python's `substitute_placeholder_defaults` over SQL its caller already normalized. +fn parse_cleaned_sql(query: &QueryColumnsInput) -> String { + if query.placeholders.is_empty() { + query.sql.clone() + } else { + placeholder_defaults(&query.sql, &query.placeholders) + } +} + +struct Tables<'a> { + request: &'a QueryColumnsRequest, + types: &'a ShapeTable, + nullability: &'a ShapeTable, +} + +impl Tables<'_> { + /// Python's `analyze_columns_and_lineage_with_polyglot` without a precomputed analysis: + /// compact analysis over the input types, every input column's nullability unknown, then + /// the parsed tree where compact analysis declines. + fn reanalysed(&self, query: &QueryColumnsInput) -> Result { + let model = ModelRequest { + query_sql: query.sql.clone(), + placeholders: query.placeholders.clone(), + references: query + .analysis_names + .iter() + .map(|analysis_name| ModelReference { + analysis_name: analysis_name.clone(), + model_ref: false, + }) + .collect(), + lineage_references: query.lineage_references.clone(), + recover_cte_facts: query.recover_cte_facts, + ..ModelRequest::default() + }; + let enriched: Enrichment = enrichment(&EnrichmentInput { + model: &model, + input_schemas: &self.request.types, + dialect: &self.request.dialect, + function_return_types: &self.request.function_return_types, + nullability_rules: self.request.nullability_rules.as_ref(), + nullability_callback: self.request.nullability_callback.as_ref(), + })?; + Ok(QueryColumns { + succeeded: enriched.analysis_succeeded, + columns: enriched.columns, + has_star: enriched.has_star, + }) + } + + /// Python's analysis of the parsed tree: `_infer_columns_from_polyglot_ast` where + /// `full_nullability`, else `_analyze_columns_and_lineage_from_polyglot_ast`. + fn parsed( + &self, + query: &QueryColumnsInput, + cleaned_sql: &str, + full_nullability: bool, + ) -> Result { + let analysis: LegacyAnalysis = catch_compiler_panic(|| { + legacy_analysis(&LegacyInput { + cleaned_sql, + lineage_references: &query.lineage_references, + recover: query.recover_cte_facts, + full_nullability, + types: self.types, + nullability: self.nullability, + profile: RecoveryProfile { + dialect: &self.request.dialect, + function_return_types: &self.request.function_return_types, + rules: self.request.nullability_rules.as_ref(), + callback: self.request.nullability_callback.as_ref(), + }, + }) + })?; + Ok(QueryColumns { + succeeded: analysis.succeeded, + columns: analysis.columns, + has_star: analysis.has_star, + }) + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 30873e3908..e99780f2dc 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -22,7 +22,7 @@ pub struct ModelReference { } /// One analysed model, with the facts Python derives from its compile input. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ModelRequest { pub name: String, /// Cursor-intrinsic analysis SQL. @@ -274,6 +274,56 @@ pub struct AnalysisSession { pub(crate) cache: Option, } +/// Which of Python's column analyses one query outside the session takes. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum QueryColumnsMode { + /// The precomputed compact batch: a failed analysis has no columns. + Batch, + /// Compact re-analysis over the input types, then the parsed tree where it declines. + Reanalysis, + /// `infer_columns_with_sql_analysis`: the parsed tree only. + Parse, + /// `analyze_columns_and_lineage_with_polyglot` with compact analysis off: the parsed tree, + /// nullability shallow where no input nullability is known. + Legacy, +} + +/// One query outside the session whose columns Python inferred. +#[derive(Debug, Clone)] +pub struct QueryColumnsInput { + /// The model SQL; in `Parse` and `Legacy` modes, already through Python's normalization. + pub sql: String, + pub placeholders: Pairs, + /// Every reference's analysis name, in reference order. + pub analysis_names: Vec, + /// Python's `lineage_reference_map` items. + pub lineage_references: Vec<(String, String, String)>, + pub recover_cte_facts: bool, + pub mode: QueryColumnsMode, +} + +/// The queries, with the profile settings and relation facts their analysis reads. +#[derive(Debug, Clone, Default)] +pub struct QueryColumnsRequest { + pub dialect: String, + pub function_return_types: Pairs, + /// Adapter nullability rules as `(function name, rule id)`. + pub nullability_rules: Option, + /// Runs the adapter's own rules, those with the `python` rule id. + pub nullability_callback: Option, + pub types: Shapes, + pub nullability: Shapes, + pub queries: Vec, +} + +/// One query's analysis: whether it succeeded, its columns and whether it projects a star. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct QueryColumns { + pub succeeded: bool, + pub columns: Option>, + pub has_star: bool, +} + /// One query's CTE fact recovery, as Python's compact enrichment runs it. #[derive(Debug, Clone)] pub struct CteFactRequest { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 189e387593..cdc93a42c7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -473,6 +473,7 @@ pub(crate) fn legacy_lines(sql: &str) -> Option> { cleaned_sql: sql, lineage_references: &references, recover: true, + full_nullability: false, types: &types, nullability: &nullability, profile: RecoveryProfile { diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs index 9dbc62ccf6..5f613005b1 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs @@ -86,7 +86,7 @@ pub(crate) fn syntax_error( } /// `substitute_placeholder_defaults`: `@@@name` becomes its default; unknown names stay. -fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> String { +pub(crate) fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> String { let mut result: String = String::with_capacity(sql.len()); let mut rest: &str = sql; while let Some(start) = rest.find(PLACEHOLDER_PREFIX) { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs index 7823d39101..11eaaf57b5 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod nullability_rules; pub(crate) mod oracle; +pub(crate) mod query_columns; pub(crate) mod registration; pub(crate) mod session; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs new file mode 100644 index 0000000000..0317b0ea58 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs @@ -0,0 +1,110 @@ +//! Python's per-query column analysis outside the session, run natively. + +use pyo3::prelude::{Bound, Py, PyAny, PyModule, PyModuleMethods, PyRef, PyResult, Python}; +use pyo3::types::PyDict; +use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::assembly::analysis_session::main::query_columns::query_columns; +use sqlbuild_analysis::assembly::analysis_session::models::{ + QueryColumns, QueryColumnsInput, QueryColumnsMode, QueryColumnsRequest, +}; +use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; +use sqlbuild_core::panics::main::native_failure::native_failure; + +use crate::bindings::_helpers::analysis_session::nullability_rules::{ + RuleFailure, nullability_callback, raised, +}; +use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::models::ProjectCatalog; +use crate::bindings::types::CompilerDetach; + +const QUERY_COLUMNS_CONTEXT: &str = "native query column analysis"; + +/// `(sql, placeholders, analysis names, lineage references, recover CTE facts, mode)`. +type QueryRow = ( + String, + Pairs, + Vec, + Vec<(String, String, String)>, + bool, + String, +); +/// `(dialect, function return types, nullability rules, types, nullability, queries)`. +type RequestRow = (String, Pairs, Pairs, Shapes, Shapes, Vec); +/// `(succeeded, [(name, type, nullability)] or None, has star)`. +type ColumnsRow = (bool, Option, String)>>, bool); + +/// Each query's columns, as Python's analysis outside the session inferred them. +#[pyfunction] +#[pyo3(signature = (catalog, request, adapter_rules=None))] +fn infer_query_columns( + py: Python<'_>, + catalog: Option>, + request: RequestRow, + adapter_rules: Option<(Py, Py)>, +) -> PyResult> { + let (dialect, function_return_types, nullability_rules, types, nullability, queries) = request; + let queries: Vec = queries + .into_iter() + .map(query_input) + .collect::>() + .map_err(|reason| compiler_error(native_failure(QUERY_COLUMNS_CONTEXT, &reason)))?; + let rule_failure: RuleFailure = RuleFailure::default(); + let request = QueryColumnsRequest { + dialect, + function_return_types, + nullability_rules: Some(nullability_rules), + nullability_callback: adapter_rules.map(|(rules, nullability)| { + nullability_callback(rules, nullability, rule_failure.clone()) + }), + types, + nullability, + queries, + }; + let catalog = catalog.as_ref().map(|catalog| &catalog.inner); + let columns = py.compiler_detach(|| query_columns(catalog, &request)); + if let Some(error) = raised(&rule_failure) { + return Err(error); + } + Ok(columns + .map_err(|reason| compiler_error(native_failure(QUERY_COLUMNS_CONTEXT, &reason)))? + .into_iter() + .map(columns_row) + .collect()) +} + +fn query_input(row: QueryRow) -> Result { + let (sql, placeholders, analysis_names, lineage_references, recover_cte_facts, mode) = row; + let mode: QueryColumnsMode = match mode.as_str() { + "batch" => QueryColumnsMode::Batch, + "reanalysis" => QueryColumnsMode::Reanalysis, + "parse" => QueryColumnsMode::Parse, + "legacy" => QueryColumnsMode::Legacy, + _ => return Err("an unknown query column analysis mode".to_owned()), + }; + Ok(QueryColumnsInput { + sql, + placeholders, + analysis_names, + lineage_references, + recover_cte_facts, + mode, + }) +} + +fn columns_row(columns: QueryColumns) -> ColumnsRow { + ( + columns.succeeded, + columns.columns.map(|columns| { + columns + .into_iter() + .map(|column| (column.name, column.data_type, column.nullability)) + .collect() + }), + columns.has_star, + ) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(infer_query_columns, module)?)?; + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs index 426d0f43eb..6e72b49d20 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs @@ -2,9 +2,10 @@ use pyo3::prelude::{Bound, PyModule, PyResult}; -use crate::bindings::_helpers::analysis_session::{oracle, session}; +use crate::bindings::_helpers::analysis_session::{oracle, query_columns, session}; pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { session::register(module)?; + query_columns::register(module)?; oracle::register(module) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 1bd30af413..358f96f11e 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -443,6 +443,18 @@ def prove_dynamic_column_contracts( ], /, ) -> list[_PivotContractRow]: ... +def infer_query_columns( + catalog: object | None, + request: tuple[ + str, + list[tuple[str, str]], + list[tuple[str, str]], + list[tuple[str, list[tuple[str, str]]]], + list[tuple[str, list[tuple[str, str]]]], + list[tuple[object, ...]], + ], + adapter_rules: tuple[Mapping[str, Callable[..., object]], type] | None = None, +) -> list[tuple[bool, list[tuple[str, str | None, str]] | None, bool]]: ... def infer_expression_source_shapes( catalog: object, request: tuple[str, bool, list[tuple[str, str]], list[tuple[str, str]], list[str]], diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py index 4ac0c0f2dc..4a93e94489 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py @@ -5,11 +5,8 @@ from typing import Any import sqlbuild._native as _native -from sqlbuild.compiler.analysis_session._helpers.session_rows import ( - contract_proof, - family_rows, - shape_rows, -) +from sqlbuild.compiler.analysis_session._helpers.profile_rows import shape_rows +from sqlbuild.compiler.analysis_session._helpers.session_rows import contract_proof, family_rows from sqlbuild.compiler.analysis_session.models import NativePivotTables from sqlbuild.compiler.analysis_session.types import ContractRow from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py index 45cbf00ac1..cce90227a3 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py @@ -2,6 +2,8 @@ from __future__ import annotations +from collections.abc import Mapping + from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.contract.types import FunctionNullabilityRule from sqlbuild.adapter.type_system.main._safe_cast_nullability import safe_cast_nullability @@ -15,6 +17,7 @@ NULLABILITY_RULE_FIRST_ARG, NULLABILITY_RULE_SAFE_CAST, ) +from sqlbuild.compiler.analysis_session.types import ShapeRows from sqlbuild.compiler.sql_analysis.constants import CASE_SENSITIVE_BINDING_DIALECTS _NULLABILITY_RULE_IDS: dict[FunctionNullabilityRule, str] = { @@ -49,3 +52,12 @@ def case_sensitive_shapes(*, profile: ExpressionInferenceProfile, dialect: str | """Python's `inferred_binding_shape` test for keeping authored identifier quoting.""" return not profile.quoted_identifiers_ignore_case and dialect in CASE_SENSITIVE_BINDING_DIALECTS + + +def shape_rows(shapes: Mapping[str, Mapping[str, str]]) -> ShapeRows: + """`{relation: {column: value}}` as ordered rows; discovery rejects non-text names and types.""" + + return [ + (name, [(column, str(value)) for column, value in shape.items()]) + for name, shape in shapes.items() + ] diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py new file mode 100644 index 0000000000..c9040d58ca --- /dev/null +++ b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py @@ -0,0 +1,120 @@ +"""Python's per-query column analysis outside the session, run natively.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import Any + +import sqlbuild._native as _native +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.adapter.contract.types import FunctionNullabilityRule +from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral +from sqlbuild.compiler.analysis_session._helpers.profile_rows import ( + adapter_nullability_rules, + nullability_rule_rows, + shape_rows, +) +from sqlbuild.compiler.analysis_session.constants import ADAPTER_NULLABILITY_CALLBACK +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns +from sqlbuild.compiler.compile._helpers.sharing.binding import lineage_reference_map +from sqlbuild.compiler.compile.models import CompileSqlReference, InferredColumn +from sqlbuild.compiler.lineage.types import InferredNullability +from sqlbuild.compiler.references.types import SqlReferenceKind +from sqlbuild.compiler.sql_analysis.constants import ( + NATIVE_DIALECT_ALIASES, + TABLE_FUNCTION_ANALYSIS_PREFIX, +) +from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog +from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql + +_BATCH_MODE: str = "batch" +_NORMALIZED_MODES: frozenset[str] = frozenset({"parse", "legacy"}) + + +def native_query_columns( + *, + queries: Sequence[NativeColumnQuery], + profile: ExpressionInferenceProfile, + column_types_by_table: Mapping[str, Mapping[str, str]], + column_nullability_by_table: Mapping[str, Mapping[str, InferredNullability]], +) -> tuple[NativeQueryColumns, ...]: + """Each query's columns; a native internal failure raises `NativeCompilerError`.""" + + if not queries: + return () + dialect: str = profile.sql_analysis_dialect or "generic" + dialect = NATIVE_DIALECT_ALIASES.get(dialect, dialect) + catalog: Any | None = ( + ( + profile.binding_catalog + or create_binding_catalog( + dialect=dialect, + quoted_ignore_case=profile.quoted_identifiers_ignore_case, + known_functions=(), + known_types=(), + relations={}, + ) + ) + if any(query.mode == _BATCH_MODE for query in queries) + else None + ) + adapter_rules: dict[str, FunctionNullabilityRule] = adapter_nullability_rules(profile) + if adapter_rules: + record_analysis_deferral(kind=ADAPTER_NULLABILITY_CALLBACK) + rows: list[tuple[bool, list[tuple[str, str | None, str]] | None, bool]] = ( + _native.infer_query_columns( + None if catalog is None else catalog.native, + ( + dialect, + list(profile.function_return_types.items()), + nullability_rule_rows(profile), + shape_rows(column_types_by_table), + shape_rows(column_nullability_by_table), + [ + _query_row(query=query, dialect=profile.sql_analysis_dialect) + for query in queries + ], + ), + (adapter_rules, InferredNullability) if adapter_rules else None, + ) + ) + return tuple( + NativeQueryColumns( + succeeded=succeeded, + columns=( + None + if columns is None + else tuple( + InferredColumn( + name=name, type=data_type, nullability=InferredNullability(nullability) + ) + for name, data_type, nullability in columns + ) + ), + has_star=has_star, + ) + for succeeded, columns, has_star in rows + ) + + +def _query_row(*, query: NativeColumnQuery, dialect: str | None) -> tuple[object, ...]: + references: tuple[CompileSqlReference, ...] = query.references + return ( + normalize_analysis_sql(sql=query.sql, dialect=dialect) + if query.mode in _NORMALIZED_MODES + else query.sql, + list((query.placeholders or {}).items()), + [_analysis_name(reference) for reference in references], + [ + (name, resource_type.value, resource_name) + for name, (resource_type, resource_name) in lineage_reference_map(references).items() + ], + query.recover_cte_facts, + query.mode, + ) + + +def _analysis_name(reference: CompileSqlReference) -> str: + if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: + return f"{TABLE_FUNCTION_ANALYSIS_PREFIX}{reference.ref_name}" + return reference.ref_name diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 827cc13352..01ffc36212 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -8,6 +8,7 @@ from sqlbuild.compiler.analysis_session._helpers.profile_rows import ( case_sensitive_shapes, nullability_rule_rows, + shape_rows, ) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( @@ -32,15 +33,6 @@ from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily -def shape_rows(shapes: Mapping[str, Mapping[str, str]]) -> ShapeRows: - """`{relation: {column: value}}` as ordered rows; discovery rejects non-text names and types.""" - - return [ - (name, [(column, str(value)) for column, value in shape.items()]) - for name, shape in shapes.items() - ] - - def session_request( *, request: NativeModelAnalysisRequest, diff --git a/src/sqlbuild/compiler/analysis_session/main/_infer_native_query_columns.py b/src/sqlbuild/compiler/analysis_session/main/_infer_native_query_columns.py new file mode 100644 index 0000000000..b4837e9689 --- /dev/null +++ b/src/sqlbuild/compiler/analysis_session/main/_infer_native_query_columns.py @@ -0,0 +1,27 @@ +"""Infer the output columns of queries outside the native analysis session.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence + +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session._helpers.query_columns import native_query_columns +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns +from sqlbuild.compiler.lineage.types import InferredNullability + + +def infer_native_query_columns( + *, + queries: Sequence[NativeColumnQuery], + profile: ExpressionInferenceProfile, + column_types_by_table: Mapping[str, Mapping[str, str]] | None = None, + column_nullability_by_table: Mapping[str, Mapping[str, InferredNullability]] | None = None, +) -> tuple[NativeQueryColumns, ...]: + """Each query's columns; a native internal failure raises `NativeCompilerError`.""" + + return native_query_columns( + queries=queries, + profile=profile, + column_types_by_table=column_types_by_table or {}, + column_nullability_by_table=column_nullability_by_table or {}, + ) diff --git a/src/sqlbuild/compiler/analysis_session/models.py b/src/sqlbuild/compiler/analysis_session/models.py index 2db27e023e..3fd27e09bc 100644 --- a/src/sqlbuild/compiler/analysis_session/models.py +++ b/src/sqlbuild/compiler/analysis_session/models.py @@ -6,9 +6,12 @@ from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session.types import QueryColumnsMode from sqlbuild.compiler.compile.models import ( AnalysisCacheContext, CompileModelInput, + CompileSqlReference, + InferredColumn, ModelSqlAnalysis, ) from sqlbuild.compiler.lineage.types import InferredNullability @@ -49,3 +52,23 @@ class NativePivotTables: authoritative_column_types_by_table: dict[str, dict[str, str]] column_nullability_by_table: dict[str, dict[str, InferredNullability]] dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]] + + +@dataclass(frozen=True, slots=True) +class NativeColumnQuery: + """One query outside the session whose output columns Python's analysis inferred.""" + + sql: str + mode: QueryColumnsMode + placeholders: dict[str, str] | None = None + references: tuple[CompileSqlReference, ...] = () + recover_cte_facts: bool = True + + +@dataclass(frozen=True, slots=True) +class NativeQueryColumns: + """One query's analysis: whether it succeeded, its columns and whether it projects a star.""" + + succeeded: bool + columns: tuple[InferredColumn, ...] | None + has_star: bool diff --git a/src/sqlbuild/compiler/analysis_session/types.py b/src/sqlbuild/compiler/analysis_session/types.py index b06a9f991e..bfa4f7e783 100644 --- a/src/sqlbuild/compiler/analysis_session/types.py +++ b/src/sqlbuild/compiler/analysis_session/types.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Literal + type ShapeRows = list[tuple[str, list[tuple[str, str]]]] type ColumnRow = tuple[str, str | None, str] type DiagnosticRow = tuple[str, str, int | None, int | None, int | None, int | None, str] @@ -24,3 +26,5 @@ ] type ContractRow = tuple[str, ProofRow | None] type FinishRow = tuple[list[OutcomeRow], ShapeRows, list[str], list[ContractRow]] + +type QueryColumnsMode = Literal["batch", "reanalysis", "parse", "legacy"] diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/binding_requests.py b/src/sqlbuild/compiler/compile/_helpers/analysis/binding_requests.py new file mode 100644 index 0000000000..35c2a57dbd --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/binding_requests.py @@ -0,0 +1,33 @@ +"""Native schema-validation requests for model SQL.""" + +from __future__ import annotations + +from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql +from sqlbuild.compiler.sql_analysis.models import ( + SqlSchemaValidationRequest, +) + + +def get_complete_schema_binding_request( + *, + query_sql: str, + placeholders: dict[str, str] | None, + dialect: str | None, + binding_schema: dict[str, dict[str, str]], + known_functions: tuple[str, ...] = (), + known_types: tuple[str, ...] = (), + cleaned_sql: str | None = None, +) -> SqlSchemaValidationRequest: + """Build one stable native schema-validation request.""" + + if cleaned_sql is None: + cleaned_sql = normalize_analysis_sql( + sql=query_sql, dialect=dialect, placeholders=placeholders + ) + return SqlSchemaValidationRequest( + sql=cleaned_sql, + dialect=dialect, + schema=binding_schema, + known_functions=known_functions, + known_types=known_types, + ) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py b/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py deleted file mode 100644 index 7a7613b533..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py +++ /dev/null @@ -1,1155 +0,0 @@ -"""Required SQL analysis-backed output inference, binding, and lineage facts.""" - -from __future__ import annotations - -import logging -import re -from collections.abc import Iterable -from typing import Any, cast - -from sqlbuild.adapter.contract.constants import POLYGLOT_CUSTOM_TYPE_NAME -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.adapter.contract.types import FunctionNullabilityRule -from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( - _polyglot_cte_passthrough_nullability_from_parsed, - _polyglot_cte_passthrough_types_from_parsed, - _polyglot_expression_is_non_null_after_filter, - _polyglot_non_null_filter_context, - _polyglot_top_level_ctes, - _unwrap_polyglot_annotations, -) -from sqlbuild.compiler.compile.constants import ( - DECIMAL_SQL_TYPE_NAME, - SQL_QUALIFIER_SEPARATOR_TOKEN, - SQL_WILDCARD_TOKEN, -) -from sqlbuild.compiler.compile.models import ( - CompiledLineageColumnFact, - CompiledLineageSourceFact, - CompileSqlReference, - InferredColumn, - NonNullFilterContext, -) -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, - InferredNullability, -) -from sqlbuild.compiler.references.types import SqlReferenceKind -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_AGGREGATE_KINDS as _POLYGLOT_AGGREGATE_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_BOOLEAN_RESULT_KINDS as _POLYGLOT_BOOLEAN_RESULT_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_CAST_KINDS as _POLYGLOT_CAST_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_JOIN_FULL as _POLYGLOT_JOIN_FULL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_JOIN_LEFT as _POLYGLOT_JOIN_LEFT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_JOIN_RIGHT as _POLYGLOT_JOIN_RIGHT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_ALIAS as _POLYGLOT_KIND_ALIAS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_CAST as _POLYGLOT_KIND_CAST, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_COALESCE as _POLYGLOT_KIND_COALESCE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_COLUMN as _POLYGLOT_KIND_COLUMN, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_COUNT as _POLYGLOT_KIND_COUNT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_IS_NULL as _POLYGLOT_KIND_IS_NULL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_LITERAL as _POLYGLOT_KIND_LITERAL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_NULL as _POLYGLOT_KIND_NULL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_SELECT as _POLYGLOT_KIND_SELECT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_TABLE as _POLYGLOT_KIND_TABLE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_TIMESTAMP as _POLYGLOT_KIND_TIMESTAMP, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_TRY_CAST as _POLYGLOT_KIND_TRY_CAST, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_ALIAS as _POLYGLOT_PAYLOAD_ALIAS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_COLUMN as _POLYGLOT_PAYLOAD_COLUMN, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_DATA_TYPE as _POLYGLOT_PAYLOAD_DATA_TYPE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_EXPRESSIONS as _POLYGLOT_PAYLOAD_EXPRESSIONS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_FROM as _POLYGLOT_PAYLOAD_FROM, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_JOINS as _POLYGLOT_PAYLOAD_JOINS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_KIND as _POLYGLOT_PAYLOAD_KIND, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_NAME as _POLYGLOT_PAYLOAD_NAME, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_PRECISION as _POLYGLOT_PAYLOAD_PRECISION, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_SCALE as _POLYGLOT_PAYLOAD_SCALE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_SELECT as _POLYGLOT_PAYLOAD_SELECT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_TABLE as _POLYGLOT_PAYLOAD_TABLE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_THIS as _POLYGLOT_PAYLOAD_THIS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_TIMEZONE as _POLYGLOT_PAYLOAD_TIMEZONE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_TO as _POLYGLOT_PAYLOAD_TO, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_SET_OPERATION_KINDS as _POLYGLOT_SET_OPERATION_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_VARCHAR_DATA_TYPES as _POLYGLOT_VARCHAR_DATA_TYPES, -) -from sqlbuild.compiler.sql_analysis.constants import ( - TIMESTAMP_WITH_TIME_ZONE_SQL_TYPE_NAME as _TIMESTAMP_WITH_TIME_ZONE_SQL_TYPE_NAME, -) -from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.diagnostics.main.log_debug_event import log_debug_event - -_DEBUG_LOGGER: logging.Logger = logging.getLogger("sqlbuild.compile") -_PLACEHOLDER_PATTERN: re.Pattern[str] = re.compile(r"@@@(\w+)") -_QUALIFIED_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile( - r'(?[^"]+)"|`(?P[^`]+)`|' - r"\[(?P[^\]]+)\]|(?P[A-Za-z_$][A-Za-z0-9_$]*))" - r"(?:\s|--[^\r\n]*(?:\r?\n|$)|/\*.*?\*/)*\.", - re.DOTALL, -) -_QUALIFIED_SCAN_CONSUMING_TOKENS: tuple[str, ...] = ('"', "`", "[", "--", "/*") -_PLAIN_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile(r"[a-z_$][a-z0-9_$]*") -_IDENTIFIER_CHARACTERS: frozenset[str] = frozenset( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$" -) - - -def _infer_columns_with_polyglot( - *, - cleaned_sql: str, - dialect: str | None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, -) -> tuple[InferredColumn, ...] | None | bool: - polyglot_module: Any = import_polyglot_sql() - try: - parsed: Any = polyglot_module.parse_one(cleaned_sql, dialect=dialect or "generic") - except polyglot_module.PolyglotError as error: - log_debug_event( - logger=_DEBUG_LOGGER, - message="column inference parse failed; falling back", - sqlbuild_error=str(error), - ) - return False - return _infer_columns_from_polyglot_ast( - parsed=parsed, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - - -def _infer_columns_from_polyglot_ast( - *, - parsed: Any, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, -) -> tuple[InferredColumn, ...] | None | bool: - infer_nullability: bool = str(getattr(parsed, "kind", "")) not in _POLYGLOT_SET_OPERATION_KINDS - select: Any | None = parsed - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - select = parsed.find(_POLYGLOT_KIND_SELECT) - if select is None or str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return None - - column_nullability_by_table = dict(column_nullability_by_table) - alias_nullability: dict[str, InferredNullability] = {} - if _has_known_nullability(column_nullability_by_table): - alias_nullability = _polyglot_alias_nullability_from_select( - select=select, - column_nullability_by_table=column_nullability_by_table, - ) - top_level_ctes: tuple[tuple[str, Any, bool], ...] = _polyglot_top_level_ctes(parsed) - referenced_table_names: tuple[str, ...] = ( - tuple( - str(getattr(table, "name", "") or "") for table in parsed.find_all(_POLYGLOT_KIND_TABLE) - ) - if top_level_ctes - else () - ) - cte_passthrough_types: dict[str, str] = _polyglot_cte_passthrough_types_from_parsed( - parsed=parsed, - column_types_by_table={}, - inference_profile=inference_profile, - expression_type_resolver=_polyglot_expression_type, - top_level_ctes=top_level_ctes, - referenced_table_names=referenced_table_names, - ) - cte_passthrough_nullability: dict[str, InferredNullability] = ( - _polyglot_cte_passthrough_nullability_from_parsed( - parsed=parsed, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - nullability_resolver=_infer_polyglot_nullability, - shallow_nullability_resolver=_infer_polyglot_shallow_nullability, - alias_nullability_resolver=_polyglot_alias_nullability_from_select, - top_level_ctes=top_level_ctes, - referenced_table_names=referenced_table_names, - ) - ) - non_null_filter_context: NonNullFilterContext | None = _polyglot_non_null_filter_context( - select=select, - column_nullability_by_table=column_nullability_by_table, - ) - columns: list[InferredColumn] = [] - projection: Any - for projection in getattr(select, "expressions", ()): - projection = _unwrap_polyglot_annotations(projection) - if bool(getattr(projection, "is_star", False)): - continue - name: str = str(getattr(projection, "output_name", "") or "") - if not name or name == SQL_WILDCARD_TOKEN: - continue - inner: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - col_type: str | None = cte_passthrough_types.get(name) or _polyglot_expression_type( - expression=inner, - inference_profile=inference_profile, - ) - nullability: InferredNullability = InferredNullability.UNKNOWN - if infer_nullability: - nullability = ( - InferredNullability.NON_NULL - if _polyglot_expression_is_non_null_after_filter( - expression=inner, - context=non_null_filter_context, - ) - else cte_passthrough_nullability.get( - name, - _infer_polyglot_nullability( - expression=inner, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ), - ) - ) - columns.append(InferredColumn(name=name, type=col_type, nullability=nullability)) - return tuple(columns) - - -def _analyze_columns_and_lineage_from_polyglot_ast( - *, - parsed: Any, - references: tuple[CompileSqlReference, ...], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - recover_cte_facts: bool, -) -> tuple[tuple[InferredColumn, ...] | None, tuple[CompiledLineageColumnFact, ...], bool]: - infer_nullability: bool = str(getattr(parsed, "kind", "")) not in _POLYGLOT_SET_OPERATION_KINDS - select: Any | None = parsed - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - select = parsed.find(_POLYGLOT_KIND_SELECT) - if select is None or str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return None, (), False - - column_nullability_by_table = dict(column_nullability_by_table) - has_known_nullability: bool = _has_known_nullability(column_nullability_by_table) - alias_nullability: dict[str, InferredNullability] = {} - if has_known_nullability: - alias_nullability = _polyglot_alias_nullability_from_select( - select=select, - column_nullability_by_table=column_nullability_by_table, - ) - alias_map: dict[str, tuple[CompiledResourceType, str]] = _polyglot_reference_alias_map( - parsed=select, - references=references, - ) - unqualified_resource: tuple[CompiledResourceType, str] | None = _single_alias_resource( - alias_map - ) - - top_level_ctes: tuple[tuple[str, Any, bool], ...] = ( - _polyglot_top_level_ctes(parsed) if recover_cte_facts else () - ) - referenced_table_names: tuple[str, ...] = ( - tuple( - str(getattr(table, "name", "") or "") for table in parsed.find_all(_POLYGLOT_KIND_TABLE) - ) - if top_level_ctes - else () - ) - cte_passthrough_types: dict[str, str] = ( - _polyglot_cte_passthrough_types_from_parsed( - parsed=parsed, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - expression_type_resolver=_polyglot_expression_type, - top_level_ctes=top_level_ctes, - referenced_table_names=referenced_table_names, - ) - if recover_cte_facts - else {} - ) - cte_passthrough_nullability: dict[str, InferredNullability] = ( - _polyglot_cte_passthrough_nullability_from_parsed( - parsed=parsed, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - nullability_resolver=_infer_polyglot_nullability, - shallow_nullability_resolver=_infer_polyglot_shallow_nullability, - alias_nullability_resolver=_polyglot_alias_nullability_from_select, - top_level_ctes=top_level_ctes, - referenced_table_names=referenced_table_names, - ) - if recover_cte_facts - else {} - ) - non_null_filter_context: NonNullFilterContext | None = _polyglot_non_null_filter_context( - select=select, - column_nullability_by_table=column_nullability_by_table, - ) - columns: list[InferredColumn] = [] - lineage_columns: list[CompiledLineageColumnFact] = [] - has_star: bool = False - projection: Any - for projection in getattr(select, "expressions", ()): - projection = _unwrap_polyglot_annotations(projection) - if bool(getattr(projection, "is_star", False)): - has_star = True - continue - inner: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - if bool(getattr(inner, "is_star", False)): - has_star = True - continue - output_column: str = str(getattr(projection, "output_name", "") or "") - if not output_column or output_column == SQL_WILDCARD_TOKEN: - continue - col_type: str | None = cte_passthrough_types.get( - output_column - ) or _polyglot_expression_type( - expression=inner, - inference_profile=inference_profile, - ) - nullability: InferredNullability = InferredNullability.UNKNOWN - if infer_nullability: - nullability = ( - InferredNullability.NON_NULL - if _polyglot_expression_is_non_null_after_filter( - expression=inner, - context=non_null_filter_context, - ) - else ( - cte_passthrough_nullability[output_column] - if output_column in cte_passthrough_nullability - else ( - _infer_polyglot_nullability( - expression=inner, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - if has_known_nullability - else _infer_polyglot_shallow_nullability( - expression=inner, - inference_profile=inference_profile, - ) - ) - ) - ) - columns.append(InferredColumn(name=output_column, type=col_type, nullability=nullability)) - - upstream_columns, confidence = _polyglot_lineage_upstream_columns( - projection=projection, - alias_map=alias_map, - unqualified_resource=unqualified_resource, - ) - transform_kind: ColumnTransformKind = _polyglot_lineage_transform_kind( - expression=inner, - has_upstream=bool(upstream_columns), - ) - lineage_columns.append( - CompiledLineageColumnFact( - output_column=output_column, - upstream_columns=upstream_columns, - transform_kind=transform_kind, - confidence=confidence - if upstream_columns or transform_kind == ColumnTransformKind.CONSTANT - else ColumnLineageConfidence.UNKNOWN, - ) - ) - return tuple(columns), tuple(lineage_columns), has_star - - -def _polyglot_reference_alias_map( - *, parsed: Any, references: tuple[CompileSqlReference, ...] -) -> dict[str, tuple[CompiledResourceType, str]]: - resource_by_name: dict[str, tuple[CompiledResourceType, str]] = {} - reference: CompileSqlReference - for reference in references: - resource_type: CompiledResourceType | None = _lineage_resource_type(reference) - if resource_type is None: - continue - resource_by_name[_analysis_reference_name(reference)] = ( - resource_type, - reference.ref_name, - ) - alias_map: dict[str, tuple[CompiledResourceType, str]] = {} - tables: tuple[Any, ...] = tuple(parsed.find_all(_POLYGLOT_KIND_TABLE)) - table: Any - for table in tables: - table_name: str = str(getattr(table, "name", "") or "") - resource: tuple[CompiledResourceType, str] | None = resource_by_name.get(table_name) - if resource is None: - continue - alias_map[table_name] = resource - alias_or_name: str = str(getattr(table, "alias_or_name", "") or "") - if alias_or_name: - alias_map[alias_or_name] = resource - return alias_map - - -def _lineage_resource_type(reference: CompileSqlReference) -> CompiledResourceType | None: - if reference.ref_kind == SqlReferenceKind.REF: - return CompiledResourceType.MODEL - if reference.ref_kind == SqlReferenceKind.SOURCE: - return CompiledResourceType.SOURCE - if reference.ref_kind == SqlReferenceKind.SEED: - return CompiledResourceType.SEED - if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: - return CompiledResourceType.TABLE_FN - return None - - -def _analysis_reference_name(reference: CompileSqlReference) -> str: - if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: - return table_function_analysis_name(reference.ref_name) - return reference.ref_name - - -def table_function_analysis_name(function_name: str) -> str: - """Return the stable relation stub used to analyze a table-function call.""" - - return f"__sqlbuild_table_function_{function_name}" - - -def _polyglot_lineage_upstream_columns( - *, - projection: Any, - alias_map: dict[str, tuple[CompiledResourceType, str]], - unqualified_resource: tuple[CompiledResourceType, str] | None, -) -> tuple[tuple[CompiledLineageSourceFact, ...], ColumnLineageConfidence]: - columns: list[CompiledLineageSourceFact] = [] - seen: set[tuple[CompiledResourceType, str, str]] = set() - confidence: ColumnLineageConfidence = ColumnLineageConfidence.HIGH - column_refs: tuple[tuple[str, str], ...] = _polyglot_column_refs_in_expression(projection) - for column_name, table_name in column_refs: - if not column_name: - continue - resource: tuple[CompiledResourceType, str] | None = None - if table_name: - resource = alias_map.get(table_name) - elif unqualified_resource is not None: - resource = unqualified_resource - confidence = ColumnLineageConfidence.MEDIUM - else: - confidence = ColumnLineageConfidence.UNKNOWN - if resource is None: - continue - resource_type, resource_name = resource - key: tuple[CompiledResourceType, str, str] = (resource_type, resource_name, column_name) - if key in seen: - continue - seen.add(key) - columns.append( - CompiledLineageSourceFact( - resource_type=resource_type, - resource_name=resource_name, - column_name=column_name, - ) - ) - return tuple(columns), confidence - - -def _single_alias_resource( - alias_map: dict[str, tuple[CompiledResourceType, str]], -) -> tuple[CompiledResourceType, str] | None: - resource: tuple[CompiledResourceType, str] | None = None - for candidate in alias_map.values(): - if resource is None: - resource = candidate - continue - if candidate[1] != resource[1]: - return None - return resource - - -def _polyglot_column_refs_in_expression(expression: Any) -> tuple[tuple[str, str], ...]: - if str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_COLUMN: - return ( - (str(getattr(expression, "name", "") or ""), _polyglot_column_table_name(expression)), - ) - payload: object = expression.to_dict() - refs: list[tuple[str, str]] = [] - - def visit(*, node: object, collected_refs: list[tuple[str, str]]) -> list[tuple[str, str]]: - if isinstance(node, dict): - node_dict: dict[str, object] = cast(dict[str, object], node) - column_payload: object = node_dict.get(_POLYGLOT_PAYLOAD_COLUMN) - if isinstance(column_payload, dict): - column_dict: dict[str, object] = cast(dict[str, object], column_payload) - column_name: str = _polyglot_name_payload_value( - column_dict.get(_POLYGLOT_PAYLOAD_NAME) - ) - table_payload: object = column_dict.get(_POLYGLOT_PAYLOAD_TABLE) - table_name: str = "" - if isinstance(table_payload, dict): - table_dict: dict[str, object] = cast(dict[str, object], table_payload) - table_name = _polyglot_name_payload_value( - table_dict.get(_POLYGLOT_PAYLOAD_NAME) - ) - return [*collected_refs, (column_name, table_name)] - for value in node_dict.values(): - collected_refs = visit(node=value, collected_refs=collected_refs) - elif isinstance(node, list): - for value in node: - collected_refs = visit(node=value, collected_refs=collected_refs) - return collected_refs - - refs = visit(node=payload, collected_refs=refs) - return tuple(refs) - - -def _has_known_nullability( - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> bool: - for column_facts in column_nullability_by_table.values(): - for value in column_facts.values(): - if value != InferredNullability.UNKNOWN: - return True - return False - - -def _polyglot_lineage_transform_kind(*, expression: Any, has_upstream: bool) -> ColumnTransformKind: - kind: str = str(getattr(expression, "kind", "")) - if bool(getattr(expression, "is_star", False)): - return ColumnTransformKind.STAR - if kind in _POLYGLOT_CAST_KINDS: - return ColumnTransformKind.CAST - if _polyglot_has_aggregation(expression): - return ColumnTransformKind.AGGREGATION - if not has_upstream: - return ColumnTransformKind.CONSTANT - if kind == _POLYGLOT_KIND_COLUMN: - return ColumnTransformKind.DIRECT - return ColumnTransformKind.EXPRESSION - - -def _polyglot_has_aggregation(expression: Any) -> bool: - nodes: tuple[Any, ...] = tuple(expression.walk()) - return any(str(getattr(node, "kind", "")) in _POLYGLOT_AGGREGATE_KINDS for node in nodes) - - -def _polyglot_expression_type( - *, expression: Any, inference_profile: ExpressionInferenceProfile -) -> str | None: - kind: str = str(getattr(expression, "kind", "")) - function_name: str = str(getattr(expression, "name", "")) or kind - function_type: str | None = ( - inference_profile.function_return_type(function_name) - if kind != _POLYGLOT_KIND_COLUMN - else None - ) - if function_type is not None: - return function_type - if kind in _POLYGLOT_BOOLEAN_RESULT_KINDS: - return "BOOLEAN" - if kind not in _POLYGLOT_CAST_KINDS: - return None - payload: object = expression.to_dict().get(kind, {}) - if not isinstance(payload, dict): - return None - target: object = payload.get(_POLYGLOT_PAYLOAD_TO) - if not isinstance(target, dict): - return None - raw_type: object = target.get(_POLYGLOT_PAYLOAD_DATA_TYPE) - if not isinstance(raw_type, str) or not raw_type: - return None - if raw_type.upper() == POLYGLOT_CUSTOM_TYPE_NAME: - custom_name: object = target.get(_POLYGLOT_PAYLOAD_NAME) - if isinstance(custom_name, str) and custom_name: - return custom_name.upper() - if ( - raw_type.lower() == _POLYGLOT_KIND_TIMESTAMP - and target.get(_POLYGLOT_PAYLOAD_TIMEZONE) is True - ): - return _TIMESTAMP_WITH_TIME_ZONE_SQL_TYPE_NAME - type_name: str = _polyglot_type_name(raw_type) - length: object = target.get("length") - if raw_type.lower() in _POLYGLOT_VARCHAR_DATA_TYPES and isinstance(length, int): - return f"VARCHAR({length})" - precision: object = target.get(_POLYGLOT_PAYLOAD_PRECISION) - scale: object = target.get(_POLYGLOT_PAYLOAD_SCALE) - if type_name == DECIMAL_SQL_TYPE_NAME and isinstance(precision, int): - if isinstance(scale, int): - return f"DECIMAL({precision}, {scale})" - return f"DECIMAL({precision})" - return type_name - - -def _polyglot_type_name(raw_type: str) -> str: - known_types: dict[str, str] = { - "big_int": "BIGINT", - "bool": "BOOLEAN", - "boolean": "BOOLEAN", - "date": "DATE", - "decimal": "DECIMAL", - "double": "DOUBLE", - "float": "FLOAT", - "int": "INT", - "integer": "INT", - "text": "TEXT", - "timestamp": "TIMESTAMP", - "var_char": "TEXT", - "varchar": "TEXT", - } - return known_types.get(raw_type, raw_type.replace("_", " ").upper()) - - -def _infer_polyglot_nullability( - *, - expression: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, -) -> InferredNullability: - kind: str = str(getattr(expression, "kind", "")) - if kind == _POLYGLOT_KIND_NULL: - return InferredNullability.NULLABLE - if kind == _POLYGLOT_KIND_LITERAL: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_COLUMN: - return _infer_polyglot_column_nullability( - expression=expression, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - ) - if kind == _POLYGLOT_KIND_CAST: - inner: Any | None = getattr(expression, "this", None) - if inner is None: - return InferredNullability.UNKNOWN - return _infer_polyglot_nullability( - expression=inner, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - if kind == _POLYGLOT_KIND_TRY_CAST: - return InferredNullability.UNKNOWN - if kind == _POLYGLOT_KIND_COUNT: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_IS_NULL: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_COALESCE: - child_nullabilities: list[InferredNullability] = [ - _infer_polyglot_nullability( - expression=child, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - for child in _polyglot_expression_args(expression) - ] - if any(value == InferredNullability.NON_NULL for value in child_nullabilities): - return InferredNullability.NON_NULL - if child_nullabilities and all( - value == InferredNullability.NULLABLE for value in child_nullabilities - ): - return InferredNullability.NULLABLE - return InferredNullability.UNKNOWN - rule: FunctionNullabilityRule | None = inference_profile.function_nullability_rule(kind) - if rule is None: - return InferredNullability.UNKNOWN - child_nullabilities: tuple[InferredNullability, ...] = tuple( - _infer_polyglot_nullability( - expression=child, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - for child in _polyglot_expression_args(expression) - ) - return rule(child_nullabilities) - - -def _infer_polyglot_shallow_nullability( - *, - expression: Any, - inference_profile: ExpressionInferenceProfile, -) -> InferredNullability: - kind: str = str(getattr(expression, "kind", "")) - if kind == _POLYGLOT_KIND_NULL: - return InferredNullability.NULLABLE - if kind == _POLYGLOT_KIND_LITERAL: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_COUNT: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_IS_NULL: - return InferredNullability.NON_NULL - if kind == _POLYGLOT_KIND_COLUMN: - return InferredNullability.UNKNOWN - if kind == _POLYGLOT_KIND_CAST: - inner: Any | None = getattr(expression, "this", None) - if inner is None: - return InferredNullability.UNKNOWN - return _infer_polyglot_shallow_nullability( - expression=inner, - inference_profile=inference_profile, - ) - if kind == _POLYGLOT_KIND_TRY_CAST: - return InferredNullability.UNKNOWN - if kind == _POLYGLOT_KIND_COALESCE: - child_nullabilities: tuple[InferredNullability, ...] = tuple( - _infer_polyglot_shallow_nullability( - expression=child, - inference_profile=inference_profile, - ) - for child in _polyglot_expression_args(expression) - ) - if any(value == InferredNullability.NON_NULL for value in child_nullabilities): - return InferredNullability.NON_NULL - if child_nullabilities and all( - value == InferredNullability.NULLABLE for value in child_nullabilities - ): - return InferredNullability.NULLABLE - return InferredNullability.UNKNOWN - rule: FunctionNullabilityRule | None = inference_profile.function_nullability_rule(kind) - if rule is None: - return InferredNullability.UNKNOWN - child_nullabilities: tuple[InferredNullability, ...] = tuple( - _infer_polyglot_shallow_nullability( - expression=child, - inference_profile=inference_profile, - ) - for child in _polyglot_expression_args(expression) - ) - return rule(child_nullabilities) - - -def _infer_polyglot_column_nullability( - *, - expression: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> InferredNullability: - column_name: str = str(getattr(expression, "name", "") or "") - if not column_name: - return InferredNullability.UNKNOWN - payload: object = expression.to_dict().get(_POLYGLOT_PAYLOAD_COLUMN, {}) - table_name: str = "" - if isinstance(payload, dict): - table_payload: object = payload.get(_POLYGLOT_PAYLOAD_TABLE) - if isinstance(table_payload, dict): - raw_name: object = table_payload.get(_POLYGLOT_PAYLOAD_NAME) - if isinstance(raw_name, str): - table_name = raw_name - if table_name: - table_fact: InferredNullability = alias_nullability.get( - table_name, InferredNullability.UNKNOWN - ) - if table_fact == InferredNullability.NULLABLE: - return InferredNullability.NULLABLE - return column_nullability_by_table.get(table_name, {}).get( - column_name, InferredNullability.UNKNOWN - ) - matches: list[InferredNullability] = [ - columns[column_name] - for columns in column_nullability_by_table.values() - if column_name in columns - ] - if len(matches) == 1: - return matches[0] - return InferredNullability.UNKNOWN - - -def _polyglot_column_table_name(column: Any) -> str: - payload: object = column.to_dict().get(_POLYGLOT_PAYLOAD_COLUMN, {}) - if not isinstance(payload, dict): - return "" - table_payload: object = payload.get(_POLYGLOT_PAYLOAD_TABLE) - if not isinstance(table_payload, dict): - return "" - raw_name: object = table_payload.get(_POLYGLOT_PAYLOAD_NAME) - return raw_name if isinstance(raw_name, str) else "" - - -def _polyglot_expression_args(expression: Any) -> tuple[Any, ...]: - args: list[Any] = [] - primary_arg: Any | None = getattr(expression, "this", None) - if primary_arg is not None: - args.append(primary_arg) - args.extend(getattr(expression, "expressions", ()) or ()) - return tuple(args) - - -def _polyglot_alias_nullability_from_select( - *, - select: Any, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> dict[str, InferredNullability]: - alias_nullability: dict[str, InferredNullability] = {} - current_aliases: set[str] = set() - - select_payload: object = select.to_dict().get(_POLYGLOT_PAYLOAD_SELECT, {}) - if not isinstance(select_payload, dict): - return alias_nullability - - from_payload: object = select_payload.get(_POLYGLOT_PAYLOAD_FROM) - if isinstance(from_payload, dict): - from_expressions: object = from_payload.get(_POLYGLOT_PAYLOAD_EXPRESSIONS) - if isinstance(from_expressions, list) and len(from_expressions) == 1: - from_table_payload: object = from_expressions[0] - if isinstance(from_table_payload, dict): - table_payload: object = from_table_payload.get(_POLYGLOT_PAYLOAD_TABLE) - if isinstance(table_payload, dict): - alias, table_name = _polyglot_table_payload_alias_and_name(table_payload) - current_aliases.add(alias) - alias_nullability[alias] = InferredNullability.UNKNOWN - _copy_table_facts_to_alias( - alias=alias, - table_name=table_name, - column_nullability_by_table=column_nullability_by_table, - ) - - joins_payload: object = select_payload.get(_POLYGLOT_PAYLOAD_JOINS) - if not isinstance(joins_payload, list): - return alias_nullability - for join_payload in joins_payload: - if not isinstance(join_payload, dict): - continue - this_payload: object = join_payload.get(_POLYGLOT_PAYLOAD_THIS) - if not isinstance(this_payload, dict): - continue - joined_table_payload: object = this_payload.get(_POLYGLOT_PAYLOAD_TABLE) - if not isinstance(joined_table_payload, dict): - continue - joined_alias, joined_table_name = _polyglot_table_payload_alias_and_name( - joined_table_payload - ) - side: str = str(join_payload.get(_POLYGLOT_PAYLOAD_KIND) or "").upper() - if side == _POLYGLOT_JOIN_LEFT: - alias_nullability[joined_alias] = InferredNullability.NULLABLE - elif side == _POLYGLOT_JOIN_RIGHT: - for alias in current_aliases: - alias_nullability[alias] = InferredNullability.NULLABLE - alias_nullability[joined_alias] = InferredNullability.UNKNOWN - elif side == _POLYGLOT_JOIN_FULL: - for alias in current_aliases: - alias_nullability[alias] = InferredNullability.NULLABLE - alias_nullability[joined_alias] = InferredNullability.NULLABLE - else: - alias_nullability[joined_alias] = InferredNullability.UNKNOWN - current_aliases.add(joined_alias) - _copy_table_facts_to_alias( - alias=joined_alias, - table_name=joined_table_name, - column_nullability_by_table=column_nullability_by_table, - ) - return alias_nullability - - -def _polyglot_table_payload_alias_and_name(table_payload: dict[str, object]) -> tuple[str, str]: - table_name: str = _polyglot_name_payload_value(table_payload.get(_POLYGLOT_PAYLOAD_NAME)) - alias: str = ( - _polyglot_name_payload_value(table_payload.get(_POLYGLOT_PAYLOAD_ALIAS)) or table_name - ) - return alias, table_name - - -def _polyglot_name_payload_value(payload: object) -> str: - if isinstance(payload, str): - return payload - if isinstance(payload, dict): - payload_dict: dict[str, object] = cast(dict[str, object], payload) - name: object = payload_dict.get(_POLYGLOT_PAYLOAD_NAME) - if isinstance(name, str): - return name - return "" - - -def substitute_placeholder_defaults(*, query_sql: str, placeholders: dict[str, str]) -> str: - """Replace @@@name tokens with their default values for SQL analysis parsing.""" - - if not placeholders: - return query_sql - - def _replacer(match: re.Match[str]) -> str: - name: str = match.group(1) - return placeholders.get(name, match.group(0)) - - return _PLACEHOLDER_PATTERN.sub(_replacer, query_sql) - - -def _replace_refs_with_stubs( - *, - query_sql: str, - dialect: str | None = None, - relation_stubs: dict[str, str] | None = None, -) -> str: - """Replace SQLBuild marker calls with parseable SQL stubs.""" - - return normalize_analysis_sql(sql=query_sql, dialect=dialect, stubs=relation_stubs) - - -def _qualified_reference_names(*, query_sql: str, reference_names: Iterable[str]) -> frozenset[str]: - """Return qualifier reference names, searching names directly when no token can hide one.""" - - if SQL_QUALIFIER_SEPARATOR_TOKEN not in query_sql: - return frozenset() - names_by_normalized: dict[str, list[str]] = {} - for value in reference_names: - name: str = str(value) - names_by_normalized.setdefault(name.casefold(), []).append(name) - if not names_by_normalized: - return frozenset() - if not query_sql.isascii() or any( - token in query_sql for token in _QUALIFIED_SCAN_CONSUMING_TOKENS - ): - return _scanned_qualified_reference_names( - query_sql=query_sql, names_by_normalized=names_by_normalized - ) - - folded_sql: str = query_sql.lower() - qualified: set[str] = set() - for normalized, names in names_by_normalized.items(): - if _PLAIN_IDENTIFIER_PATTERN.fullmatch(normalized) is not None and _is_qualifier( - folded_sql=folded_sql, name=normalized - ): - qualified.update(names) - return frozenset(qualified) - - -def _is_qualifier(*, folded_sql: str, name: str) -> bool: - length: int = len(folded_sql) - start: int = folded_sql.find(name) - while start != -1: - end: int = start + len(name) - if (start == 0 or folded_sql[start - 1] not in _IDENTIFIER_CHARACTERS) and ( - end == length or folded_sql[end] not in _IDENTIFIER_CHARACTERS - ): - while end < length and folded_sql[end].isspace(): - end += 1 - if end < length and folded_sql[end] == SQL_QUALIFIER_SEPARATOR_TOKEN: - return True - start = folded_sql.find(name, start + 1) - return False - - -def _scanned_qualified_reference_names( - *, query_sql: str, names_by_normalized: dict[str, list[str]] -) -> frozenset[str]: - qualified: set[str] = set() - for match in _QUALIFIED_IDENTIFIER_PATTERN.finditer(query_sql): - identifier: str = next(value for value in match.groups() if value is not None) - qualified.update(names_by_normalized.get(identifier.casefold(), ())) - return frozenset(qualified) - - -def _infer_expression_nullability( - *, - expression: Any, - expressions_module: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, -) -> InferredNullability: - """Infer only nullability facts SQLBuild can prove statically.""" - - literal_type: type[Any] = expressions_module.Literal - null_type: type[Any] = expressions_module.Null - column_type: type[Any] = expressions_module.Column - cast_type: type[Any] = expressions_module.Cast - try_cast_type: type[Any] = expressions_module.TryCast - coalesce_type: type[Any] = expressions_module.Coalesce - count_type: type[Any] = expressions_module.Count - - if isinstance(expression, null_type): - return InferredNullability.NULLABLE - if isinstance(expression, literal_type): - return InferredNullability.NON_NULL - if isinstance(expression, column_type): - return _infer_column_nullability( - column=expression, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - ) - if isinstance(expression, cast_type): - return _infer_expression_nullability( - expression=expression.this, - expressions_module=expressions_module, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - if isinstance(expression, try_cast_type): - return InferredNullability.UNKNOWN - if isinstance(expression, count_type): - return InferredNullability.NON_NULL - if isinstance(expression, coalesce_type): - return _infer_coalesce_nullability( - expression=expression, - expressions_module=expressions_module, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - function_name: str = _expression_function_name(expression) - rule: FunctionNullabilityRule | None = inference_profile.function_nullability_rule( - function_name - ) - if rule is None: - return InferredNullability.UNKNOWN - arg_nullabilities: tuple[InferredNullability, ...] = tuple( - _infer_expression_nullability( - expression=arg, - expressions_module=expressions_module, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - for arg in _expression_function_args(expression) - ) - return rule(arg_nullabilities) - - -def _infer_column_nullability( - *, - column: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> InferredNullability: - table_name: str = str(column.table or "") - column_name: str = str(column.name or "") - if table_name: - table_fact: InferredNullability = alias_nullability.get( - table_name, InferredNullability.UNKNOWN - ) - if table_fact == InferredNullability.NULLABLE: - return InferredNullability.NULLABLE - return column_nullability_by_table.get(table_name, {}).get( - column_name, InferredNullability.UNKNOWN - ) - - matches: list[InferredNullability] = [ - column_facts[column_name] - for column_facts in column_nullability_by_table.values() - if column_name in column_facts - ] - if len(matches) == 1: - return matches[0] - return InferredNullability.UNKNOWN - - -def _infer_coalesce_nullability( - *, - expression: Any, - expressions_module: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, -) -> InferredNullability: - arg_nullabilities: tuple[InferredNullability, ...] = tuple( - _infer_expression_nullability( - expression=arg, - expressions_module=expressions_module, - alias_nullability=alias_nullability, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - ) - for arg in expression.expressions - ) - if any(value == InferredNullability.NON_NULL for value in arg_nullabilities): - return InferredNullability.NON_NULL - if arg_nullabilities and all( - value == InferredNullability.NULLABLE for value in arg_nullabilities - ): - return InferredNullability.NULLABLE - return InferredNullability.UNKNOWN - - -def _expression_function_name(expression: Any) -> str: - sql_name: object | None = getattr(expression, "sql_name", None) - if callable(sql_name): - return str(sql_name()).upper() - key: object | None = getattr(expression, "key", None) - return str(key or "").upper() - - -def _expression_function_args(expression: Any) -> tuple[Any, ...]: - args: list[Any] = [] - primary_arg: Any | None = getattr(expression, "this", None) - if primary_arg is not None: - args.append(primary_arg) - args.extend(expression.expressions) - return tuple(args) - - -def _copy_table_facts_to_alias( - *, - alias: str, - table_name: str, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> None: - if alias == table_name: - return - table_facts: dict[str, InferredNullability] | None = column_nullability_by_table.get(table_name) - if table_facts is not None: - alias_facts: dict[str, dict[str, InferredNullability]] = column_nullability_by_table - alias_facts.setdefault(alias, table_facts) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/compact.py b/src/sqlbuild/compiler/compile/_helpers/analysis/compact.py deleted file mode 100644 index c2b40d58ef..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/compact.py +++ /dev/null @@ -1,1834 +0,0 @@ -"""Required SQL analysis-backed output inference, binding, and lineage facts.""" - -from __future__ import annotations - -import logging -import threading -from collections.abc import Iterator, Sequence -from contextlib import contextmanager -from contextvars import ContextVar, Token -from dataclasses import replace -from typing import Any, cast - -import orjson - -import sqlbuild._native as _native -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.columns import ( - _analysis_reference_name, - _analyze_columns_and_lineage_from_polyglot_ast, - _infer_columns_with_polyglot, - _infer_polyglot_nullability, - _infer_polyglot_shallow_nullability, - _polyglot_alias_nullability_from_select, - _polyglot_expression_type, - _qualified_reference_names, - _replace_refs_with_stubs, - substitute_placeholder_defaults, -) -from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( - _polyglot_cte_passthrough_facts, - _polyglot_filtered_non_null_outputs, -) -from sqlbuild.compiler.compile._helpers.sharing.binding import ( - binding_query_fields, - lineage_reference_map, - normalized_analysis_sql, - prepare_binding_queries, - relation_stubs_collide, - remembered_shared_results, - reused_shared_results, - shared_result_is_exact, - shared_result_keys, -) -from sqlbuild.compiler.compile.constants import ( - COMPACT_ANALYSIS_FACT_LENGTH, - COMPACT_ANALYSIS_LEGACY_RESPONSE_LENGTH, - COMPACT_ANALYSIS_RESPONSE_LENGTH, - COMPACT_ANALYSIS_SOURCE_LENGTH, - COMPACT_RELATION_STUB_PREFIX, - RESOLVED_SOURCE_CONFIDENCE, - SQL_WILDCARD_TOKEN, - UNKNOWN_SQL_TYPE_NAME, -) -from sqlbuild.compiler.compile.exceptions import CompactAnalysisInputError -from sqlbuild.compiler.compile.models import ( - CompactBatchContext, - CompactBatchExecutionOptions, - CompactBatchInputs, - CompactBatchPreparation, - CompactLineageFacts, - CompactProjectedFacts, - CompactProjectionCaches, - CompiledLineageColumnFact, - CompiledLineageSourceFact, - CompileSqlReference, - CteFactResolvers, - InferredColumn, - NativeCompactAnalysis, - PolyglotAnalysisResult, - PreparedBindingQuery, - ProjectedAnalysisRequest, - SharedBindingQuery, -) -from sqlbuild.compiler.compile.types import CompactBatchResponseCallback, CompiledResourceType -from sqlbuild.compiler.lineage.types import ( - ColumnLineageConfidence, - ColumnTransformKind, - InferredNullability, -) -from sqlbuild.compiler.profiling.main.record import record_compile_timing -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_BASE_TABLES as _POLYGLOT_ANALYSIS_BASE_TABLES, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_CAST_TYPE as _POLYGLOT_ANALYSIS_CAST_TYPE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_FUNCTION_NAME as _POLYGLOT_ANALYSIS_FUNCTION_NAME, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_IS_STAR as _POLYGLOT_ANALYSIS_IS_STAR, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_NAME as _POLYGLOT_ANALYSIS_NAME, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_NULLABILITY as _POLYGLOT_ANALYSIS_NULLABILITY, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_NULLABILITY_NON_NULL as _POLYGLOT_ANALYSIS_NULLABILITY_NON_NULL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_NULLABILITY_NULLABLE as _POLYGLOT_ANALYSIS_NULLABILITY_NULLABLE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_PROJECTIONS as _POLYGLOT_ANALYSIS_PROJECTIONS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_RELATIONS as _POLYGLOT_ANALYSIS_RELATIONS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SHAPE as _POLYGLOT_ANALYSIS_SHAPE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SHAPE_SELECT as _POLYGLOT_ANALYSIS_SHAPE_SELECT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SHAPE_SET_OPERATION as _POLYGLOT_ANALYSIS_SHAPE_SET_OPERATION, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SOURCE_ALIAS as _POLYGLOT_ANALYSIS_SOURCE_ALIAS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SOURCE_CONFIDENCE as _POLYGLOT_ANALYSIS_SOURCE_CONFIDENCE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_SOURCE_NAME as _POLYGLOT_ANALYSIS_SOURCE_NAME, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_STAR_PROJECTIONS as _POLYGLOT_ANALYSIS_STAR_PROJECTIONS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TABLE as _POLYGLOT_ANALYSIS_TABLE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_AGGREGATION as _POLYGLOT_ANALYSIS_TRANSFORM_AGGREGATION, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_CAST as _POLYGLOT_ANALYSIS_TRANSFORM_CAST, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_CONSTANT as _POLYGLOT_ANALYSIS_TRANSFORM_CONSTANT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_DIRECT as _POLYGLOT_ANALYSIS_TRANSFORM_DIRECT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_FUNCTION as _POLYGLOT_ANALYSIS_TRANSFORM_FUNCTION, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_KIND as _POLYGLOT_ANALYSIS_TRANSFORM_KIND, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TRANSFORM_STAR as _POLYGLOT_ANALYSIS_TRANSFORM_STAR, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_TYPE_HINT as _POLYGLOT_ANALYSIS_TYPE_HINT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_UNSAFE_TRANSFORMS as _POLYGLOT_ANALYSIS_UNSAFE_TRANSFORMS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_UPSTREAM as _POLYGLOT_ANALYSIS_UPSTREAM, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_ALIAS as _POLYGLOT_PAYLOAD_ALIAS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_COLUMN as _POLYGLOT_PAYLOAD_COLUMN, -) -from sqlbuild.compiler.sql_analysis.exceptions import SqlAnalysisBoundaryError -from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog -from sqlbuild.compiler.sql_analysis.main._decode_schema_validation import decode_schema_validation -from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql -from sqlbuild.compiler.sql_analysis.main._normalize_analysis_batch import ( - normalize_analysis_sql_results, -) -from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql -from sqlbuild.compiler.sql_analysis.models import ( - SqlBindingDiagnostic, - SqlBindingResult, - SqlSchemaValidationRequest, -) -from sqlbuild.compiler.sql_analysis.types import ( - NativeCompactAnalysisJob, - NativeProjectCatalog, - NativeQueryAnalysisModule, -) -from sqlbuild.diagnostics.main.log_debug_event import log_debug_event - -_DEBUG_LOGGER: logging.Logger = logging.getLogger("sqlbuild.compile") -_ANALYSIS_LOCK: ContextVar[threading.Condition | None] = ContextVar( - "sqlbuild_model_analysis_lock", default=None -) -_COMPACT_ANALYSIS_WORKERS: int = 4 -_NATIVE_LEGACY_FALLBACK: str = "native project type recovery requires legacy fallback" - - -@contextmanager -def serialized_analysis(lock: threading.Condition) -> Iterator[None]: - """Mark the held `lock` as the guard of every Python analysis step in this context.""" - - token: Token[threading.Condition | None] = _ANALYSIS_LOCK.set(lock) - try: - yield - finally: - _ANALYSIS_LOCK.reset(token) - - -@contextmanager -def native_section() -> Iterator[None]: - """Release the analysis lock around native work that reads no shared analysis state.""" - - lock: threading.Condition | None = _ANALYSIS_LOCK.get() - if lock is None: - yield - return - lock.release() - try: - yield - finally: - _ = lock.acquire() - - -def infer_columns_with_sql_analysis( - *, - query_sql: str, - placeholders: dict[str, str] | None = None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, - inference_profile: ExpressionInferenceProfile | None = None, -) -> tuple[InferredColumn, ...] | None: - """Infer output columns from model query SQL using SQL analysis.""" - - profile: ExpressionInferenceProfile = inference_profile or ExpressionInferenceProfile() - - cleaned_sql: str = _replace_refs_with_stubs( - query_sql=query_sql, - dialect=profile.sql_analysis_dialect, - ) - if placeholders: - cleaned_sql = substitute_placeholder_defaults( - query_sql=cleaned_sql, placeholders=placeholders - ) - - polyglot_columns: tuple[InferredColumn, ...] | None | bool = _infer_columns_with_polyglot( - cleaned_sql=cleaned_sql, - dialect=profile.sql_analysis_dialect, - column_nullability_by_table=column_nullability_by_table or {}, - inference_profile=profile, - ) - if isinstance(polyglot_columns, tuple): - return polyglot_columns - return None - - -def analyze_columns_and_lineage_with_polyglot( - *, - query_sql: str, - references: tuple[CompileSqlReference, ...] = (), - placeholders: dict[str, str] | None = None, - column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, - column_types_by_table: dict[str, dict[str, str]] | None = None, - inference_profile: ExpressionInferenceProfile | None = None, - allow_compact_analysis: bool = False, - binding_schema: dict[str, dict[str, str]] | None = None, - recover_cte_facts: bool = False, - precomputed: NativeCompactAnalysis | None = None, -) -> PolyglotAnalysisResult: - """Infer columns and compact lineage facts from one Polyglot parse.""" - - profile: ExpressionInferenceProfile = inference_profile or ExpressionInferenceProfile() - cleaned_sql: str = ( - precomputed.cleaned_sql - if precomputed is not None - else _cleaned_analysis_sql( - query_sql=query_sql, - placeholders=placeholders, - dialect=profile.sql_analysis_dialect, - ) - ) - if precomputed is not None and precomputed.projected: - if precomputed.analysis is None and precomputed.compact_rows is None: - return PolyglotAnalysisResult( - analysis_succeeded=False, - binding_diagnostics=precomputed.binding_diagnostics or (), - binding_validated=binding_schema is not None, - ) - return _projected_analysis_result( - request=ProjectedAnalysisRequest( - analysis=precomputed.analysis, - compact_rows=precomputed.compact_rows, - compact_fact_rows=precomputed.compact_fact_rows, - string_pool=precomputed.string_pool, - caches=CompactProjectionCaches( - columns=precomputed.compact_column_cache, - facts=precomputed.compact_fact_cache, - decoded_facts=precomputed.compact_decoded_fact_cache, - ), - template_index=precomputed.compact_template_index, - resource_name_indexes=precomputed.resource_name_indexes, - resource_names=precomputed.resource_names, - binding_diagnostics=( - precomputed.binding_diagnostics - if precomputed.binding_diagnostics is not None - else _validate_complete_binding_schema( - cleaned_sql=cleaned_sql, - dialect=profile.sql_analysis_dialect, - binding_schema=binding_schema, - ) - ), - binding_validated=binding_schema is not None, - ) - ) - polyglot_module: Any = import_polyglot_sql() - compact_result: ( - tuple[tuple[InferredColumn, ...] | None, tuple[CompiledLineageColumnFact, ...], bool] | None - ) = _analyze_columns_and_lineage_with_compact_polyglot( - polyglot_module=polyglot_module, - cleaned_sql=cleaned_sql, - dialect=profile.sql_analysis_dialect, - references=references, - column_nullability_by_table=column_nullability_by_table or {}, - column_types_by_table=column_types_by_table or {}, - inference_profile=profile, - allow_compact_analysis=( - allow_compact_analysis - and not ( - precomputed is not None - and not precomputed.projected - and precomputed.analysis is None - ) - ), - recover_cte_facts=recover_cte_facts, - analysis=precomputed.analysis if precomputed is not None else None, - ) - if compact_result is not None: - return PolyglotAnalysisResult( - analysis_succeeded=True, - columns=compact_result[0], - lineage_columns=compact_result[1], - has_star=compact_result[2], - binding_diagnostics=( - precomputed.binding_diagnostics - if precomputed is not None and precomputed.binding_diagnostics is not None - else _validate_complete_binding_schema( - cleaned_sql=cleaned_sql, - dialect=profile.sql_analysis_dialect, - binding_schema=binding_schema, - ) - ), - binding_validated=binding_schema is not None, - ) - try: - parsed: Any = polyglot_module.parse_one( - cleaned_sql, - dialect=profile.sql_analysis_dialect or "generic", - ) - except polyglot_module.PolyglotError as error: - log_debug_event( - logger=_DEBUG_LOGGER, - message="column and lineage analysis parse failed; falling back", - sqlbuild_error=str(error), - ) - return PolyglotAnalysisResult(analysis_succeeded=False) - columns, legacy_lineage_columns, has_star = _analyze_columns_and_lineage_from_polyglot_ast( - parsed=parsed, - references=references, - column_nullability_by_table=column_nullability_by_table or {}, - column_types_by_table=column_types_by_table or {}, - inference_profile=profile, - recover_cte_facts=recover_cte_facts, - ) - lineage_columns: Sequence[CompiledLineageColumnFact] = legacy_lineage_columns - if precomputed is not None and precomputed.compact_rows is not None: - lineage_columns = _compact_projected_analysis_result( - request=ProjectedAnalysisRequest( - analysis=None, - compact_rows=precomputed.compact_rows, - compact_fact_rows=precomputed.compact_fact_rows, - string_pool=precomputed.string_pool, - caches=CompactProjectionCaches( - columns=precomputed.compact_column_cache, - facts=precomputed.compact_fact_cache, - decoded_facts=precomputed.compact_decoded_fact_cache, - ), - template_index=precomputed.compact_template_index, - resource_name_indexes=precomputed.resource_name_indexes, - resource_names=precomputed.resource_names, - binding_diagnostics=(), - binding_validated=False, - ) - ).lineage_columns - return PolyglotAnalysisResult( - analysis_succeeded=True, - columns=columns, - lineage_columns=lineage_columns, - has_star=has_star, - binding_diagnostics=( - precomputed.binding_diagnostics - if precomputed is not None and precomputed.binding_diagnostics is not None - else _validate_complete_binding_schema( - cleaned_sql=cleaned_sql, - dialect=profile.sql_analysis_dialect, - binding_schema=binding_schema, - ) - ), - binding_validated=binding_schema is not None, - ) - - -def analyze_queries_with_compact_polyglot_batch( - *, - query_sqls: tuple[str, ...], - references: tuple[tuple[CompileSqlReference, ...], ...], - placeholders: tuple[dict[str, str] | None, ...], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - recover_cte_facts: tuple[bool, ...], - rich_type_inference: bool = True, - cached_batch: tuple[CompactBatchPreparation, object] | None = None, - execution: CompactBatchExecutionOptions | None = None, -) -> tuple[NativeCompactAnalysis, ...]: - """Analyze rendered SQL in one bounded native call, preserving input order.""" - - options: CompactBatchExecutionOptions = execution or CompactBatchExecutionOptions() - binding_schemas: tuple[dict[str, dict[str, str]] | None, ...] | None = options.binding_schemas - - if not (len(query_sqls) == len(references) == len(placeholders) == len(recover_cte_facts)): - raise CompactAnalysisInputError( - "compact query-analysis batch inputs must have equal lengths" - ) - if binding_schemas is not None and len(binding_schemas) != len(query_sqls): - raise CompactAnalysisInputError("binding schemas must match the query batch length") - if cached_batch is not None: - if len(cached_batch[0].cleaned_sql) != len(query_sqls): - raise CompactAnalysisInputError( - "cached compact query-analysis preparation has an invalid length" - ) - with record_compile_timing("analysis_projection_ms"): - return _project_compact_analysis_batch( - preparation=cached_batch[0], response_payload=cached_batch[1] - ) - inputs: CompactBatchInputs = CompactBatchInputs( - query_sqls=query_sqls, - references=references, - placeholders=placeholders, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - recover_cte_facts=recover_cte_facts, - rich_type_inference=rich_type_inference, - binding_schemas=binding_schemas, - ) - reuse_results: bool = options.on_response is None and binding_schemas is not None - with record_compile_timing("analysis_preparation_ms"): - context: CompactBatchContext = _compact_batch_context( - inputs=inputs, - share=True, - shareable_prekeys=options.shareable_prekeys if reuse_results else None, - ) - memo: dict[object, object] | None = ( - cast(dict[object, object], context.binding_catalog.shared_analyses) - if reuse_results and context.binding_catalog is not None - else None - ) - memo_keys: tuple[tuple[object, ...] | None, ...] = shared_result_keys( - inputs=inputs, context=context - ) - reused: dict[int, NativeCompactAnalysis] = ( - reused_shared_results(inputs=inputs, context=context, keys=memo_keys, memo=memo) - if memo is not None - else {} - ) - analyzed: tuple[int, ...] = tuple( - index for index in range(len(query_sqls)) if index not in reused - ) - analyzed_results: tuple[NativeCompactAnalysis, ...] = () - if analyzed: - analyzed_inputs: CompactBatchInputs = _batch_input_subset(inputs=inputs, indexes=analyzed) - recomputed: tuple[int, ...] - analyzed_results, recomputed = _analyze_compact_inputs( - inputs=analyzed_inputs, - context=_batch_context_subset(context=context, indexes=analyzed), - on_response=options.on_response if not reused else None, - ) - if memo is not None: - for key, result in remembered_shared_results( - keys=tuple( - None if position in recomputed else memo_keys[index] - for position, index in enumerate(analyzed) - ), - results=analyzed_results, - ).items(): - memo.setdefault(key, result) - results: list[NativeCompactAnalysis | None] = [None] * len(query_sqls) - for index, result in zip(analyzed, analyzed_results, strict=True): - results[index] = result - for index, result in reused.items(): - results[index] = result - return tuple(cast(NativeCompactAnalysis, result) for result in results) - - -def _analyze_compact_inputs( - *, - inputs: CompactBatchInputs, - context: CompactBatchContext, - on_response: CompactBatchResponseCallback | None, -) -> tuple[tuple[NativeCompactAnalysis, ...], tuple[int, ...]]: - """Run one native batch, re-analysing inexact shared members with their own names.""" - - with record_compile_timing("analysis_preparation_ms"): - preparation: CompactBatchPreparation = _prepare_compact_analysis_batch( - inputs=inputs, context=context - ) - with record_compile_timing("analysis_native_ms"): - response_payload: object = _run_compact_analysis_batch(preparation=preparation) - with record_compile_timing("analysis_projection_ms"): - projected: tuple[NativeCompactAnalysis, ...] = _attach_compiled_bindings( - preparation=preparation, - response=response_payload, - analyses=_project_compact_analysis_batch( - preparation=preparation, - response_payload=response_payload, - ), - ) - member_indexes: tuple[int, ...] = _inexact_shared_members( - preparation=preparation, response=response_payload - ) - if member_indexes: - projected = _reanalyze_shared_members( - member_indexes=member_indexes, projected=projected, request=inputs - ) - elif on_response is not None: - on_response(preparation=preparation, response=response_payload) - return projected, member_indexes - - -def _batch_input_subset( - *, inputs: CompactBatchInputs, indexes: tuple[int, ...] -) -> CompactBatchInputs: - if len(indexes) == len(inputs.query_sqls): - return inputs - return replace( - inputs, - query_sqls=tuple(inputs.query_sqls[index] for index in indexes), - references=tuple(inputs.references[index] for index in indexes), - placeholders=tuple(inputs.placeholders[index] for index in indexes), - recover_cte_facts=tuple(inputs.recover_cte_facts[index] for index in indexes), - binding_schemas=( - tuple(inputs.binding_schemas[index] for index in indexes) - if inputs.binding_schemas is not None - else None - ), - ) - - -def _batch_context_subset( - *, context: CompactBatchContext, indexes: tuple[int, ...] -) -> CompactBatchContext: - if len(indexes) == len(context.normalized_sqls): - return context - return replace( - context, - normalized_sqls=[context.normalized_sqls[index] for index in indexes], - binding_queries=tuple(context.binding_queries[index] for index in indexes), - ) - - -def _inexact_shared_members( - *, preparation: CompactBatchPreparation, response: object -) -> tuple[int, ...]: - if not preparation.shared_query_indexes or not isinstance(response, dict): - return () - payload: dict[str, object] = cast(dict[str, object], response) - validations: object = payload.get("validations") - analyses: object = payload.get("analyses") - templates: object = payload.get("templates") - if not isinstance(analyses, list) or not isinstance(templates, list): - return () - members: list[int] = [] - for index, (projection, analysis) in enumerate( - zip(preparation.projections, analyses, strict=True) - ): - template_index: int = cast(int, projection["templateIndex"]) - query_index: int = cast(int, preparation.templates[template_index]["queryIndex"]) - if query_index not in preparation.shared_query_indexes: - continue - template: object = ( - templates[analysis[0]] - if isinstance(analysis, list) - and analysis - and isinstance(analysis[0], int) - and 0 <= analysis[0] < len(templates) - else str(analysis) - ) - validation: object = ( - cast(list[object], validations)[query_index] if isinstance(validations, list) else None - ) - if not shared_result_is_exact(validation=validation, template=template): - members.append(index) - return tuple(members) - - -def _reanalyze_shared_members( - *, - member_indexes: tuple[int, ...], - projected: tuple[NativeCompactAnalysis, ...], - request: CompactBatchInputs, -) -> tuple[NativeCompactAnalysis, ...]: - """Recompute members of inexact shared queries with their own relation names.""" - - with record_compile_timing("analysis_preparation_ms"): - preparation: CompactBatchPreparation = _prepare_compact_analysis_batch( - inputs=_batch_input_subset(inputs=request, indexes=member_indexes), - share_binding_queries=False, - ) - with record_compile_timing("analysis_native_ms"): - response: object = _run_compact_analysis_batch(preparation=preparation) - with record_compile_timing("analysis_projection_ms"): - replacements: tuple[NativeCompactAnalysis, ...] = _attach_compiled_bindings( - preparation=preparation, - response=response, - analyses=_project_compact_analysis_batch( - preparation=preparation, response_payload=response - ), - ) - results: list[NativeCompactAnalysis] = list(projected) - for index, replacement in zip(member_indexes, replacements, strict=True): - results[index] = replacement - return tuple(results) - - -def _compact_batch_context( - *, - inputs: CompactBatchInputs, - share: bool, - shareable_prekeys: frozenset[str] | None, -) -> CompactBatchContext: - """Prepare the catalog, cleaned SQL, and binding references for one batch.""" - - profile: ExpressionInferenceProfile = inputs.inference_profile - dialect: str | None = profile.sql_analysis_dialect - binding_catalog: Any | None = profile.binding_catalog - if binding_catalog is None and inputs.binding_schemas is not None: - binding_catalog = create_binding_catalog( - dialect=dialect or "generic", - quoted_ignore_case=profile.quoted_identifiers_ignore_case, - known_functions=profile.semantic_known_functions, - known_types=profile.semantic_known_types, - relations=inputs.column_types_by_table, - ) - if binding_catalog is not None: - required_tables: set[str] = set() - for query_references in inputs.references: - required_tables.update( - _analysis_reference_name(reference) for reference in query_references - ) - binding_catalog.prepare_analysis( - types={ - name: inputs.column_types_by_table[name] - for name in required_tables - if name in inputs.column_types_by_table - }, - nullability={ - name: inputs.column_nullability_by_table[name] - for name in required_tables - if name in inputs.column_nullability_by_table - }, - ) - requests: list[tuple[str, None, dict[str, str] | None]] = [ - (query_sql, None, query_placeholders) - for query_sql, query_placeholders in zip( - inputs.query_sqls, inputs.placeholders, strict=True - ) - ] - results: list[str | Exception] = normalize_analysis_sql_results( - dialect=dialect, - requests=requests, - catalog=binding_catalog.native if binding_catalog is not None else None, - ) - normalized_sqls: list[str] = [normalized_analysis_sql(result) for result in results] - binding_queries: tuple[PreparedBindingQuery | None, ...] = ( - prepare_binding_queries( - binding_catalog=binding_catalog, - inputs=inputs, - cleaned_sqls=normalized_sqls, - share=share, - shareable_prekeys=shareable_prekeys, - ) - if inputs.binding_schemas is not None and binding_catalog is not None - else (None,) * len(inputs.query_sqls) - ) - return CompactBatchContext( - binding_catalog=binding_catalog, - normalized_sqls=normalized_sqls, - binding_queries=binding_queries, - ) - - -def _prepare_compact_analysis_batch( - *, - inputs: CompactBatchInputs, - share_binding_queries: bool = True, - context: CompactBatchContext | None = None, -) -> CompactBatchPreparation: - query_sqls: tuple[str, ...] = inputs.query_sqls - references: tuple[tuple[CompileSqlReference, ...], ...] = inputs.references - column_nullability_by_table: dict[str, dict[str, InferredNullability]] = ( - inputs.column_nullability_by_table - ) - column_types_by_table: dict[str, dict[str, str]] = inputs.column_types_by_table - inference_profile: ExpressionInferenceProfile = inputs.inference_profile - recover_cte_facts: tuple[bool, ...] = inputs.recover_cte_facts - rich_type_inference: bool = inputs.rich_type_inference - binding_schemas: tuple[dict[str, dict[str, str]] | None, ...] | None = inputs.binding_schemas - dialect: str | None = inference_profile.sql_analysis_dialect - if context is None: - context = _compact_batch_context( - inputs=inputs, share=share_binding_queries, shareable_prekeys=None - ) - binding_catalog: Any | None = context.binding_catalog - prepared: list[str] = [] - queries: list[dict[str, object]] = [] - query_indexes: dict[tuple[object, ...], int] = {} - templates: list[dict[str, object]] = [] - template_indexes: dict[ - tuple[ - int, - tuple[tuple[str, str], ...], - tuple[tuple[str, str], ...], - tuple[str, ...], - bool, - bool, - ], - int, - ] = {} - projections: list[dict[str, object]] = [] - function_return_types: dict[str, str] = dict(inference_profile.function_return_types) - normalized_sqls: list[str] = context.normalized_sqls - member_binding_schemas: tuple[dict[str, dict[str, str]] | None, ...] = ( - binding_schemas if binding_schemas is not None else (None,) * len(query_sqls) - ) - binding_queries: tuple[PreparedBindingQuery | None, ...] = context.binding_queries - shared_query_indexes: set[int] = set() - members: list[tuple[dict[str, tuple[CompiledResourceType, str]], dict[str, str]]] - stubbed_sqls: dict[int, str | Exception] - members, stubbed_sqls = _member_analysis_stubs( - inputs=inputs, - context=context, - binding_schemas=member_binding_schemas, - ) - for index, ( - cleaned_sql, - query_references, - query_recover_cte_facts, - binding_schema, - binding_query, - (lineage_references, canonical_stubs), - ) in enumerate( - zip( - normalized_sqls, - references, - recover_cte_facts, - member_binding_schemas, - binding_queries, - members, - strict=True, - ) - ): - shared: SharedBindingQuery | None = ( - binding_query.shared if binding_query is not None else None - ) - analysis_sql: str = ( - shared.sql - if shared is not None - else cleaned_sql - if binding_schema is not None - else normalized_analysis_sql(stubbed_sqls[index]) - ) - query: dict[str, object] = { - "sql": analysis_sql, - "dialect": dialect or "generic", - } - schema: dict[str, object] | None = None - if binding_catalog is not None: - query["analysis_references"] = [ - (name, canonical_stubs.get(name, name)) - for name in sorted( - lineage_references, key=lambda name: canonical_stubs.get(name, name) - ) - ] - else: - schema = _compact_analysis_schema( - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - table_names=frozenset( - _analysis_reference_name(reference) for reference in query_references - ), - table_name_aliases=canonical_stubs, - ) - if schema is not None: - query["schema"] = schema - binding_payload: dict[str, object] | None = None - shared_key: tuple[object, ...] | None = ( - (shared.key, query_recover_cte_facts) if shared is not None else None - ) - query_index: int | None = query_indexes.get(shared_key) if shared_key is not None else None - if query_index is None and binding_schema is not None: - if binding_query is None or binding_catalog is None: - raise SqlAnalysisBoundaryError("binding schemas require a native project catalog") - binding_fields: dict[str, object] - binding_fields, binding_payload = binding_query_fields( - binding_query=binding_query, binding_catalog=binding_catalog - ) - query.update(binding_fields) - query_key: tuple[object, ...] - if shared_key is not None: - query_key = shared_key - else: - query_key = ( - analysis_sql, - dialect or "generic", - ( - orjson.dumps( - schema or query.get("analysis_references"), option=orjson.OPT_SORT_KEYS - ) - ), - ( - orjson.dumps(binding_payload, option=orjson.OPT_SORT_KEYS) - if binding_payload is not None - else None - ), - ) - query_index = query_indexes.get(query_key) - if query_index is None: - query_index = len(queries) - query_indexes[query_key] = query_index - queries.append(query) - if shared is not None: - shared_query_indexes.add(query_index) - declared_column_order: tuple[str, ...] = ( - tuple( - column_nullability_by_table.get(_analysis_reference_name(query_references[0]), {}) - ) - if len(query_references) == 1 - else () - ) - reference_types: tuple[tuple[str, str], ...] = tuple( - (canonical_stubs[name], resource_type.value) - for name, (resource_type, _) in lineage_references.items() - ) - template_key: tuple[ - int, - tuple[tuple[str, str], ...], - tuple[tuple[str, str], ...], - tuple[str, ...], - bool, - bool, - ] = ( - query_index, - reference_types, - tuple(function_return_types.items()), - declared_column_order, - query_recover_cte_facts, - rich_type_inference, - ) - template_index: int | None = template_indexes.get(template_key) - if template_index is None: - template_index = len(templates) - template_indexes[template_key] = template_index - templates.append( - { - "queryIndex": query_index, - "references": { - name: { - "resourceType": resource_type, - "resourceName": name, - } - for name, resource_type in reference_types - }, - "functionReturnTypes": function_return_types, - "declaredColumnOrder": list(declared_column_order), - "recoverCteFacts": query_recover_cte_facts, - "richTypeInference": rich_type_inference, - } - ) - projections.append( - { - "templateIndex": template_index, - "resourceNames": { - canonical_stubs[name]: resource_name - for name, (_, resource_name) in lineage_references.items() - }, - } - ) - prepared.append(cleaned_sql) - return CompactBatchPreparation( - cleaned_sql=tuple(prepared), - queries=tuple(queries), - templates=tuple(templates), - projections=tuple(projections), - binding_catalog=binding_catalog, - shared_query_indexes=frozenset(shared_query_indexes), - ) - - -def _member_analysis_stubs( - *, - inputs: CompactBatchInputs, - context: CompactBatchContext, - binding_schemas: tuple[dict[str, dict[str, str]] | None, ...], -) -> tuple[ - list[tuple[dict[str, tuple[CompiledResourceType, str]], dict[str, str]]], - dict[int, str | Exception], -]: - """Return each member's relation stubs and, in one native batch, its stubbed SQL.""" - - members: list[tuple[dict[str, tuple[CompiledResourceType, str]], dict[str, str]]] = [] - stubbed_requests: dict[int, tuple[str, dict[str, str], dict[str, str] | None]] = {} - for index, ( - query_sql, - cleaned_sql, - query_references, - query_placeholders, - member_schema, - member_query, - ) in enumerate( - zip( - inputs.query_sqls, - context.normalized_sqls, - inputs.references, - inputs.placeholders, - binding_schemas, - context.binding_queries, - strict=True, - ) - ): - member: tuple[dict[str, tuple[CompiledResourceType, str]], dict[str, str]] = ( - _member_relation_stubs( - cleaned_sql=cleaned_sql, - references=query_references, - binding_schema=member_schema, - binding_query=member_query, - ) - ) - members.append(member) - if member_schema is None and (member_query is None or member_query.shared is None): - stubbed_requests[index] = (query_sql, member[1], query_placeholders) - stubbed_sqls: list[str | Exception] = normalize_analysis_sql_results( - dialect=inputs.inference_profile.sql_analysis_dialect, - requests=tuple(stubbed_requests.values()), - catalog=(context.binding_catalog.native if context.binding_catalog is not None else None), - ) - return members, dict(zip(stubbed_requests, stubbed_sqls, strict=True)) - - -def _member_relation_stubs( - *, - cleaned_sql: str, - references: tuple[CompileSqlReference, ...], - binding_schema: dict[str, dict[str, str]] | None, - binding_query: PreparedBindingQuery | None, -) -> tuple[dict[str, tuple[CompiledResourceType, str]], dict[str, str]]: - """Return a member's lineage references and the relation names its analysis SQL uses.""" - - lineage_references: dict[str, tuple[CompiledResourceType, str]] = lineage_reference_map( - references - ) - if binding_query is not None and binding_query.shared is not None: - return lineage_references, binding_query.shared.stubs - stubbed_reference_names: frozenset[str] = ( - frozenset() - if binding_schema is not None - or relation_stubs_collide(cleaned_sql=cleaned_sql, names=lineage_references) - else frozenset(lineage_references) - - _qualified_reference_names( - query_sql=cleaned_sql, - reference_names=lineage_references.keys(), - ) - ) - return lineage_references, { - name: f"{COMPACT_RELATION_STUB_PREFIX}{index}" if name in stubbed_reference_names else name - for index, name in enumerate(lineage_references) - } - - -def _run_compact_analysis_batch(*, preparation: CompactBatchPreparation) -> object: - """Analyse every project size with the full worker pool; the rules phase sets peak memory.""" - - workers: int = _COMPACT_ANALYSIS_WORKERS - if preparation.binding_catalog is not None: - job: NativeCompactAnalysisJob = cast( - NativeProjectCatalog, preparation.binding_catalog.native - ).prepare_compact( - orjson.dumps( - { - "queries": preparation.queries, - "templates": preparation.templates, - "projections": preparation.projections, - "workers": workers, - } - ) - ) - with native_section(): - return orjson.loads(job.run()) - request_json: str = orjson.dumps( - { - "queries": preparation.queries, - "templates": preparation.templates, - "projections": preparation.projections, - "workers": workers, - }, - option=orjson.OPT_SORT_KEYS, - ).decode() - with native_section(): - return orjson.loads( - cast(NativeQueryAnalysisModule, _native).analyze_project_queries_compact_json( - request_json - ) - ) - - -def _attach_compiled_bindings( - *, - preparation: CompactBatchPreparation, - response: object, - analyses: tuple[NativeCompactAnalysis, ...], -) -> tuple[NativeCompactAnalysis, ...]: - if not isinstance(response, dict): - return analyses - validations: object = cast(dict[str, object], response).get("validations") - if validations is None: - return analyses - if not isinstance(validations, list) or len(validations) != len(preparation.queries): - raise SqlAnalysisBoundaryError("native compilation returned an invalid binding batch") - results: list[NativeCompactAnalysis] = [] - for analysis, projection in zip(analyses, preparation.projections, strict=True): - template_index: int = cast(int, projection["templateIndex"]) - query_index: int = cast(int, preparation.templates[template_index]["queryIndex"]) - validation: object = validations[query_index] - if validation is None: - results.append(analysis) - continue - binding: SqlBindingResult = decode_schema_validation( - sql=analysis.cleaned_sql, - dialect=cast(str, preparation.queries[query_index]["dialect"]), - response=validation, - ) - results.append(replace(analysis, binding_diagnostics=binding.diagnostics)) - return tuple(results) - - -def _project_compact_analysis_batch( - *, preparation: CompactBatchPreparation, response_payload: object -) -> tuple[NativeCompactAnalysis, ...]: - if not isinstance(response_payload, dict): - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid batch response" - ) - response_mapping: dict[str, object] = cast(dict[str, object], response_payload) - raw_strings: object = response_mapping.get("strings") - raw_facts: object = response_mapping.get("facts") - templates: object = response_mapping.get("templates") - responses: object = response_mapping.get("analyses") - if ( - not isinstance(raw_strings, list) - or not all(isinstance(value, str) for value in raw_strings) - or not isinstance(raw_facts, list) - or not isinstance(templates, list) - or not isinstance(responses, list) - or len(responses) != len(preparation.cleaned_sql) - ): - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid batch response" - ) - string_pool: tuple[str, ...] = tuple(cast(list[str], raw_strings)) - column_cache: dict[tuple[int, int | None, int], InferredColumn] = {} - fact_cache: dict[int, CompactProjectedFacts] = {} - decoded_fact_cache: dict[ - int, - tuple[InferredColumn, tuple[int, int, int, tuple[tuple[int, int, int], ...]]], - ] = {} - results: list[NativeCompactAnalysis] = [] - for cleaned_sql, response in zip(preparation.cleaned_sql, responses, strict=True): - if ( - isinstance(response, list) - and len(response) == COMPACT_ANALYSIS_RESPONSE_LENGTH - and isinstance(response[0], int) - and not isinstance(response[0], bool) - and isinstance(response[1], list) - ): - template_index: int = response[0] - if template_index < 0: - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid template index" - ) - try: - template: object = templates[template_index] - except IndexError as error: - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid template index" - ) from error - if isinstance(template, str): - log_debug_event( - logger=_DEBUG_LOGGER, - message="native compact query analysis failed", - sqlbuild_error=template, - ) - results.append( - NativeCompactAnalysis( - cleaned_sql=cleaned_sql, - analysis=None, - projected=template != _NATIVE_LEGACY_FALLBACK, - ) - ) - continue - legacy_type_recovery: bool = ( - isinstance(template, list) - and len(template) == COMPACT_ANALYSIS_LEGACY_RESPONSE_LENGTH - and template[2] == _NATIVE_LEGACY_FALLBACK - ) - if not ( - isinstance(template, list) - and ( - len(template) == COMPACT_ANALYSIS_RESPONSE_LENGTH - or legacy_type_recovery - or ( - len(template) == COMPACT_ANALYSIS_LEGACY_RESPONSE_LENGTH - and isinstance(template[2], bool) - ) - ) - and isinstance(template[0], list) - and isinstance(template[1], bool) - ): - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid template" - ) - resource_name_indexes: dict[int, int] = {} - for raw_mapping in response[1]: - if not ( - isinstance(raw_mapping, list) - and len(raw_mapping) == COMPACT_ANALYSIS_RESPONSE_LENGTH - and all( - isinstance(value, int) and not isinstance(value, bool) - for value in raw_mapping - ) - ): - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an invalid resource mapping" - ) - canonical_index, resource_index = cast(tuple[int, int], tuple(raw_mapping)) - try: - _ = string_pool[canonical_index] - _ = string_pool[resource_index] - except IndexError as error: - raise SqlAnalysisBoundaryError( - "native compact query analysis returned an out-of-range resource index" - ) from error - resource_name_indexes[canonical_index] = resource_index - results.append( - NativeCompactAnalysis( - cleaned_sql=cleaned_sql, - analysis=None - if legacy_type_recovery - else { - "hasStar": template[1], - "starResolved": len(template) == COMPACT_ANALYSIS_LEGACY_RESPONSE_LENGTH - and template[2] is True, - }, - projected=not legacy_type_recovery, - compact_rows=cast(list[object], template[0]), - compact_fact_rows=cast(list[object], raw_facts), - string_pool=string_pool, - compact_column_cache=column_cache, - compact_template_index=template_index, - compact_fact_cache=fact_cache, - compact_decoded_fact_cache=decoded_fact_cache, - resource_name_indexes=resource_name_indexes, - ) - ) - continue - log_debug_event( - logger=_DEBUG_LOGGER, - message="native compact query analysis failed", - sqlbuild_error=str(response or "invalid native response"), - ) - results.append( - NativeCompactAnalysis(cleaned_sql=cleaned_sql, analysis=None, projected=True) - ) - return tuple(results) - - -def _projected_analysis_result(*, request: ProjectedAnalysisRequest) -> PolyglotAnalysisResult: - analysis: dict[str, Any] | None = request.analysis - if request.compact_rows is not None: - return _compact_projected_analysis_result(request=request) - if analysis is None: - raise SqlAnalysisBoundaryError("native project query analysis omitted facts") - raw_columns: object = analysis.get("columns") - raw_lineage: object = analysis.get("lineageColumns") - if not isinstance(raw_columns, list) or not isinstance(raw_lineage, list): - raise SqlAnalysisBoundaryError("native project query analysis returned invalid facts") - columns: list[InferredColumn] = [] - for raw_column in raw_columns: - if not isinstance(raw_column, dict): - raise SqlAnalysisBoundaryError("native project query analysis returned invalid columns") - name: object = raw_column.get("name") - if not isinstance(name, str): - raise SqlAnalysisBoundaryError("native project query analysis omitted a column name") - data_type: object = raw_column.get("type") - columns.append( - InferredColumn( - name=name, - type=data_type if isinstance(data_type, str) else None, - nullability=InferredNullability(str(raw_column.get("nullability") or "unknown")), - ) - ) - lineage_columns: list[CompiledLineageColumnFact] = [] - for raw_column in raw_lineage: - if not isinstance(raw_column, dict): - raise SqlAnalysisBoundaryError("native project query analysis returned invalid lineage") - output_column: object = raw_column.get("outputColumn") - raw_upstream: object = raw_column.get("upstreamColumns") - if not isinstance(output_column, str) or not isinstance(raw_upstream, list): - raise SqlAnalysisBoundaryError("native project query analysis omitted lineage fields") - upstream: list[CompiledLineageSourceFact] = [] - for raw_source in raw_upstream: - if not isinstance(raw_source, dict): - raise SqlAnalysisBoundaryError( - "native project query analysis returned invalid upstream lineage" - ) - upstream.append( - CompiledLineageSourceFact( - resource_type=str(raw_source.get("resourceType") or ""), - resource_name=str(raw_source.get("resourceName") or ""), - column_name=str(raw_source.get("columnName") or ""), - ) - ) - lineage_columns.append( - CompiledLineageColumnFact( - output_column=output_column, - upstream_columns=tuple(upstream), - transform_kind=ColumnTransformKind( - str(raw_column.get("transformKind") or "unknown") - ), - confidence=ColumnLineageConfidence(str(raw_column.get("confidence") or "unknown")), - ) - ) - return PolyglotAnalysisResult( - analysis_succeeded=True, - columns=tuple(columns), - lineage_columns=tuple(lineage_columns), - has_star=bool(analysis.get("hasStar")), - star_resolved=bool(analysis.get("starResolved")), - binding_diagnostics=request.binding_diagnostics, - binding_validated=request.binding_validated, - ) - - -def _compact_projected_analysis_result( - *, request: ProjectedAnalysisRequest -) -> PolyglotAnalysisResult: - rows: list[object] = request.compact_rows or [] - fact_rows: list[object] = request.compact_fact_rows or [] - string_pool: tuple[str, ...] = request.string_pool - cached_facts: CompactProjectedFacts | None = request.caches.fact(request.template_index) - if cached_facts is not None: - return PolyglotAnalysisResult( - analysis_succeeded=True, - columns=cached_facts.columns, - lineage_columns=CompactLineageFacts( - string_pool=string_pool, - rows=cached_facts.lineage_rows, - resource_name_indexes=request.resource_name_indexes, - resource_names=request.resource_names, - ), - has_star=bool(request.analysis and request.analysis.get("hasStar")), - star_resolved=bool(request.analysis and request.analysis.get("starResolved")), - binding_diagnostics=request.binding_diagnostics, - binding_validated=request.binding_validated, - ) - nullability_by_code: tuple[InferredNullability, ...] = ( - InferredNullability.UNKNOWN, - InferredNullability.NON_NULL, - InferredNullability.NULLABLE, - ) - transform_by_code: tuple[ColumnTransformKind, ...] = ( - ColumnTransformKind.DIRECT, - ColumnTransformKind.CAST, - ColumnTransformKind.EXPRESSION, - ColumnTransformKind.AGGREGATION, - ColumnTransformKind.STAR, - ColumnTransformKind.CONSTANT, - ) - confidence_by_code: tuple[ColumnLineageConfidence, ...] = ( - ColumnLineageConfidence.UNKNOWN, - ColumnLineageConfidence.HIGH, - ColumnLineageConfidence.MEDIUM, - ) - - def pooled_string(value: object) -> str: - if not isinstance(value, int) or isinstance(value, bool): - raise SqlAnalysisBoundaryError("native compact analysis returned an invalid index") - try: - return string_pool[value] - except IndexError as error: - raise SqlAnalysisBoundaryError( - "native compact analysis returned an out-of-range index" - ) from error - - columns: list[InferredColumn] = [] - compact_lineage_rows: list[tuple[int, int, int, tuple[tuple[int, int, int], ...]]] = [] - for raw_fact_index in rows: - if ( - not isinstance(raw_fact_index, int) - or isinstance(raw_fact_index, bool) - or raw_fact_index < 0 - ): - raise SqlAnalysisBoundaryError("native compact analysis returned an invalid fact index") - decoded_fact: ( - tuple[ - InferredColumn, - tuple[int, int, int, tuple[tuple[int, int, int], ...]], - ] - | None - ) = request.caches.decoded_fact(raw_fact_index) - if decoded_fact is not None: - column, lineage_row = decoded_fact - columns.append(column) - compact_lineage_rows.append(lineage_row) - continue - try: - raw_row: object = fact_rows[raw_fact_index] - except IndexError as error: - raise SqlAnalysisBoundaryError( - "native compact analysis returned an out-of-range fact index" - ) from error - if not isinstance(raw_row, list) or len(raw_row) != COMPACT_ANALYSIS_FACT_LENGTH: - raise SqlAnalysisBoundaryError("native compact analysis returned an invalid column") - name_index, type_index, nullability_code, transform_code, confidence_code, raw_upstream = ( - raw_row - ) - if ( - not isinstance(nullability_code, int) - or isinstance(nullability_code, bool) - or not isinstance(transform_code, int) - or isinstance(transform_code, bool) - or not isinstance(confidence_code, int) - or isinstance(confidence_code, bool) - or not isinstance(raw_upstream, list) - ): - raise SqlAnalysisBoundaryError("native compact analysis returned invalid column facts") - try: - nullability: InferredNullability = nullability_by_code[nullability_code] - _ = transform_by_code[transform_code] - _ = confidence_by_code[confidence_code] - except IndexError as error: - raise SqlAnalysisBoundaryError( - "native compact analysis returned an invalid fact code" - ) from error - if not isinstance(name_index, int) or isinstance(name_index, bool): - raise SqlAnalysisBoundaryError("native compact analysis returned an invalid name index") - if type_index is not None and ( - not isinstance(type_index, int) or isinstance(type_index, bool) - ): - raise SqlAnalysisBoundaryError("native compact analysis returned an invalid type index") - column_key: tuple[int, int | None, int] = (name_index, type_index, nullability_code) - column: InferredColumn | None = request.caches.column(column_key) - if column is None: - column = InferredColumn( - name=pooled_string(name_index), - type=pooled_string(type_index) if type_index is not None else None, - nullability=nullability, - ) - request.caches.remember_column(key=column_key, column=column) - columns.append(column) - upstream: list[tuple[int, int, int]] = [] - for raw_source in raw_upstream: - if ( - not isinstance(raw_source, list) - or len(raw_source) != COMPACT_ANALYSIS_SOURCE_LENGTH - ): - raise SqlAnalysisBoundaryError( - "native compact analysis returned invalid upstream lineage" - ) - if ( - type(raw_source[0]) is not int - or type(raw_source[1]) is not int - or type(raw_source[2]) is not int - ): - raise SqlAnalysisBoundaryError( - "native compact analysis returned invalid upstream indexes" - ) - source_key: tuple[int, int, int] = cast(tuple[int, int, int], tuple(raw_source)) - _ = pooled_string(source_key[0]) - _ = pooled_string(source_key[1]) - _ = pooled_string(source_key[2]) - upstream.append(source_key) - lineage_row: tuple[int, int, int, tuple[tuple[int, int, int], ...]] = ( - name_index, - transform_code, - confidence_code, - tuple(upstream), - ) - compact_lineage_rows.append(lineage_row) - request.caches.remember_decoded_fact(fact_index=raw_fact_index, fact=(column, lineage_row)) - compact_facts: CompactProjectedFacts = CompactProjectedFacts( - columns=tuple(columns), - lineage_rows=tuple(compact_lineage_rows), - ) - request.caches.remember_fact(template_index=request.template_index, fact=compact_facts) - return PolyglotAnalysisResult( - analysis_succeeded=True, - columns=compact_facts.columns, - lineage_columns=CompactLineageFacts( - string_pool=string_pool, - rows=compact_facts.lineage_rows, - resource_name_indexes=request.resource_name_indexes, - resource_names=request.resource_names, - ), - has_star=bool(request.analysis and request.analysis.get("hasStar")), - star_resolved=bool(request.analysis and request.analysis.get("starResolved")), - binding_diagnostics=request.binding_diagnostics, - binding_validated=request.binding_validated, - ) - - -def _cleaned_analysis_sql( - *, - query_sql: str, - placeholders: dict[str, str] | None, - dialect: str | None, - relation_stubs: dict[str, str] | None = None, -) -> str: - return normalize_analysis_sql( - sql=query_sql, dialect=dialect, stubs=relation_stubs, placeholders=placeholders - ) - - -def get_complete_schema_binding_request( - *, - query_sql: str, - placeholders: dict[str, str] | None, - dialect: str | None, - binding_schema: dict[str, dict[str, str]], - known_functions: tuple[str, ...] = (), - known_types: tuple[str, ...] = (), - cleaned_sql: str | None = None, -) -> SqlSchemaValidationRequest: - """Build one stable native schema-validation request.""" - - if cleaned_sql is None: - cleaned_sql = normalize_analysis_sql( - sql=query_sql, dialect=dialect, placeholders=placeholders - ) - return SqlSchemaValidationRequest( - sql=cleaned_sql, - dialect=dialect, - schema=binding_schema, - known_functions=known_functions, - known_types=known_types, - ) - - -def _validate_complete_binding_schema( - *, - cleaned_sql: str, - dialect: str | None, - binding_schema: dict[str, dict[str, str]] | None, -) -> tuple[SqlBindingDiagnostic, ...]: - if binding_schema is None: - return () - return get_schema_validations( - requests=( - SqlSchemaValidationRequest( - sql=cleaned_sql, - dialect=dialect, - schema=binding_schema, - ), - ) - )[0].diagnostics - - -def _analyze_columns_and_lineage_with_compact_polyglot( - *, - polyglot_module: Any, - cleaned_sql: str, - dialect: str | None, - references: tuple[CompileSqlReference, ...], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - allow_compact_analysis: bool, - recover_cte_facts: bool, - analysis: dict[str, Any] | None = None, -) -> tuple[tuple[InferredColumn, ...] | None, tuple[CompiledLineageColumnFact, ...], bool] | None: - if not allow_compact_analysis: - return None - try: - options: dict[str, object] = {"dialect": dialect or "generic"} - schema: dict[str, object] | None = _compact_analysis_schema( - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - table_names=frozenset(_analysis_reference_name(reference) for reference in references), - ) - if schema is not None: - options["schema"] = schema - if analysis is None: - analysis = polyglot_module.analyze_query(cleaned_sql, options) - except polyglot_module.PolyglotError as error: - log_debug_event( - logger=_DEBUG_LOGGER, - message="compact query analysis failed; falling back", - sqlbuild_error=str(error), - ) - return None - if not isinstance(analysis, dict): - return None - projections: object = analysis.get(_POLYGLOT_ANALYSIS_PROJECTIONS) - if not isinstance(projections, list): - return None - if not _compact_analysis_is_eligible(analysis=analysis, projections=projections): - return None - reference_map: dict[str, tuple[CompiledResourceType, str]] = lineage_reference_map(references) - relation_alias_by_name: dict[str, str | None] = _compact_relation_alias_by_name(analysis) - cte_passthrough_types, cte_passthrough_nullability, direct_cte_outputs, parsed = ( - _polyglot_cte_passthrough_facts( - polyglot_module=polyglot_module, - cleaned_sql=cleaned_sql, - dialect=dialect, - column_types_by_table=column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - analysis=analysis, - resolvers=CteFactResolvers( - expression_type=_polyglot_expression_type, - nullability=_infer_polyglot_nullability, - shallow_nullability=_infer_polyglot_shallow_nullability, - alias_nullability=_polyglot_alias_nullability_from_select, - ), - ) - if recover_cte_facts - else ({}, {}, frozenset(), None) - ) - filtered_non_null_outputs: frozenset[str] = _polyglot_filtered_non_null_outputs( - polyglot_module=polyglot_module, - cleaned_sql=cleaned_sql, - dialect=dialect, - analysis=analysis, - column_nullability_by_table=column_nullability_by_table, - parsed=parsed, - ) - columns: list[InferredColumn] = [] - lineage_columns: list[CompiledLineageColumnFact] = [] - has_star: bool = _compact_analysis_has_star(analysis) - infer_nullability: bool = ( - analysis.get(_POLYGLOT_ANALYSIS_SHAPE) != _POLYGLOT_ANALYSIS_SHAPE_SET_OPERATION - ) - for projection in projections: - if not isinstance(projection, dict): - return None - if bool(projection.get(_POLYGLOT_ANALYSIS_IS_STAR)): - continue - output_column: str = str(projection.get(_POLYGLOT_ANALYSIS_NAME) or "") - if not output_column or output_column == SQL_WILDCARD_TOKEN: - continue - columns.append( - InferredColumn( - name=output_column, - type=( - cte_passthrough_types.get(output_column) - if output_column in direct_cte_outputs - else _compact_projection_type( - projection=projection, inference_profile=inference_profile - ) - ), - nullability=( - InferredNullability.NON_NULL - if output_column in filtered_non_null_outputs - else ( - ( - cte_passthrough_nullability.get(output_column) - if output_column in direct_cte_outputs - else None - ) - or _compact_projection_nullability( - projection=projection, - infer_nullability=infer_nullability, - ) - ) - ), - ) - ) - upstream_columns, confidence = _compact_lineage_upstream_columns( - projection=projection, - reference_map=reference_map, - relation_alias_by_name=relation_alias_by_name, - ) - transform_kind: ColumnTransformKind = _compact_transform_kind( - projection=projection, - has_upstream=bool(upstream_columns), - ) - lineage_columns.append( - CompiledLineageColumnFact( - output_column=output_column, - upstream_columns=upstream_columns, - transform_kind=transform_kind, - confidence=confidence - if upstream_columns or transform_kind == ColumnTransformKind.CONSTANT - else ColumnLineageConfidence.UNKNOWN, - ) - ) - if has_star and len(references) == 1: - schema_name: str = _analysis_reference_name(references[0]) - declared_order: dict[str, int] = { - column_name: index - for index, column_name in enumerate(column_nullability_by_table.get(schema_name, {})) - } - fallback_order: int = len(declared_order) - columns.sort(key=lambda column: declared_order.get(column.name, fallback_order)) - lineage_columns.sort( - key=lambda column: declared_order.get(column.output_column, fallback_order) - ) - return tuple(columns), tuple(lineage_columns), has_star - - -def _compact_analysis_is_eligible(*, analysis: dict[str, Any], projections: list[object]) -> bool: - shape: object = analysis.get(_POLYGLOT_ANALYSIS_SHAPE) - if shape not in {_POLYGLOT_ANALYSIS_SHAPE_SELECT, _POLYGLOT_ANALYSIS_SHAPE_SET_OPERATION}: - return False - projection: object - for projection in projections: - if not isinstance(projection, dict): - return False - projection_dict: dict[str, Any] = cast(dict[str, Any], projection) - transform_kind: str = str(projection_dict.get(_POLYGLOT_ANALYSIS_TRANSFORM_KIND) or "") - if transform_kind in _POLYGLOT_ANALYSIS_UNSAFE_TRANSFORMS: - return False - upstream_values: object = projection_dict.get(_POLYGLOT_ANALYSIS_UPSTREAM) - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_CAST and ( - not isinstance(upstream_values, list) or not upstream_values - ): - return False - return True - - -def _compact_analysis_schema( - *, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - column_types_by_table: dict[str, dict[str, str]], - table_names: frozenset[str] | None = None, - table_name_aliases: dict[str, str] | None = None, -) -> dict[str, object] | None: - tables: list[dict[str, object]] = [] - table_name: str - columns: dict[str, InferredNullability] - selected_tables: tuple[tuple[str, dict[str, InferredNullability]], ...] = ( - tuple(sorted(column_nullability_by_table.items())) - if table_names is None - else tuple( - (table_name, column_nullability_by_table.get(table_name, {})) - for table_name in sorted( - table_names, - key=lambda name: (table_name_aliases or {}).get(name, name), - ) - ) - ) - for table_name, columns in selected_tables: - if not columns: - continue - column_types: dict[str, str] = column_types_by_table.get(table_name, {}) - tables.append( - { - "name": (table_name_aliases or {}).get(table_name, table_name), - "columns": [ - _compact_analysis_schema_column( - column_name=column_name, - column_type=column_types.get(column_name, "UNKNOWN"), - nullability=columns[column_name], - ) - for column_name in columns - ], - } - ) - if not tables: - return None - return {"tables": tables} - - -def _compact_analysis_schema_column( - *, - column_name: str, - column_type: str, - nullability: InferredNullability, -) -> dict[str, object]: - column: dict[str, object] = {"name": column_name, "type": column_type} - if nullability == InferredNullability.NON_NULL: - column["nullable"] = False - elif nullability == InferredNullability.NULLABLE: - column["nullable"] = True - return column - - -def _compact_relation_alias_by_name(analysis: dict[str, Any]) -> dict[str, str | None]: - alias_by_name: dict[str, str | None] = {} - relation_key: str - for relation_key in (_POLYGLOT_ANALYSIS_RELATIONS, _POLYGLOT_ANALYSIS_BASE_TABLES): - relations: object = analysis.get(relation_key) - if not isinstance(relations, list): - continue - relation: object - for relation in relations: - if not isinstance(relation, dict): - continue - name: object = relation.get(_POLYGLOT_ANALYSIS_NAME) - if not isinstance(name, str) or not name: - continue - alias: object = relation.get(_POLYGLOT_PAYLOAD_ALIAS) - alias_by_name[name] = alias if isinstance(alias, str) and alias else None - return alias_by_name - - -def _compact_analysis_has_star(analysis: dict[str, Any]) -> bool: - star_projections: object = analysis.get(_POLYGLOT_ANALYSIS_STAR_PROJECTIONS) - return isinstance(star_projections, list) and bool(star_projections) - - -def _compact_projection_type( - *, projection: dict[str, Any], inference_profile: ExpressionInferenceProfile -) -> str | None: - cast_type: object = projection.get(_POLYGLOT_ANALYSIS_CAST_TYPE) - if isinstance(cast_type, str) and cast_type and cast_type != UNKNOWN_SQL_TYPE_NAME: - return cast_type - transform_function: object = projection.get(_POLYGLOT_ANALYSIS_TRANSFORM_FUNCTION) - if isinstance(transform_function, dict): - function_name: object = transform_function.get(_POLYGLOT_ANALYSIS_FUNCTION_NAME) - if isinstance(function_name, str): - declared_type: str | None = inference_profile.function_return_type(function_name) - if declared_type is not None: - return declared_type - type_hint: object = projection.get(_POLYGLOT_ANALYSIS_TYPE_HINT) - if isinstance(type_hint, str) and type_hint and type_hint != UNKNOWN_SQL_TYPE_NAME: - return type_hint - return None - - -def _compact_projection_nullability( - *, - projection: dict[str, Any], - infer_nullability: bool, -) -> InferredNullability: - if not infer_nullability: - return InferredNullability.UNKNOWN - value: object = projection.get(_POLYGLOT_ANALYSIS_NULLABILITY) - if value == _POLYGLOT_ANALYSIS_NULLABILITY_NON_NULL: - return InferredNullability.NON_NULL - if value == _POLYGLOT_ANALYSIS_NULLABILITY_NULLABLE: - return InferredNullability.NULLABLE - return InferredNullability.UNKNOWN - - -def _compact_lineage_upstream_columns( - *, - projection: dict[str, Any], - reference_map: dict[str, tuple[CompiledResourceType, str]], - relation_alias_by_name: dict[str, str | None], -) -> tuple[tuple[CompiledLineageSourceFact, ...], ColumnLineageConfidence]: - upstream_values: object = projection.get(_POLYGLOT_ANALYSIS_UPSTREAM) - if not isinstance(upstream_values, list): - return (), ColumnLineageConfidence.UNKNOWN - columns: list[CompiledLineageSourceFact] = [] - seen: set[tuple[CompiledResourceType, str, str]] = set() - confidence: ColumnLineageConfidence = ColumnLineageConfidence.HIGH - upstream: object - for upstream in upstream_values: - if not isinstance(upstream, dict): - continue - column_name: object = upstream.get(_POLYGLOT_PAYLOAD_COLUMN) - if not isinstance(column_name, str) or not column_name or column_name == SQL_WILDCARD_TOKEN: - continue - source_name: object = upstream.get(_POLYGLOT_ANALYSIS_SOURCE_NAME) or upstream.get( - _POLYGLOT_ANALYSIS_TABLE - ) - if not isinstance(source_name, str) or not source_name: - confidence = ColumnLineageConfidence.UNKNOWN - continue - resource: tuple[CompiledResourceType, str] | None = reference_map.get(source_name) - if resource is None: - continue - source_confidence: object = upstream.get(_POLYGLOT_ANALYSIS_SOURCE_CONFIDENCE) - source_alias: object = upstream.get(_POLYGLOT_ANALYSIS_SOURCE_ALIAS) - if source_confidence != RESOLVED_SOURCE_CONFIDENCE and not isinstance(source_alias, str): - confidence = ColumnLineageConfidence.MEDIUM - resource_type, resource_name = resource - key: tuple[CompiledResourceType, str, str] = (resource_type, resource_name, column_name) - if key in seen: - continue - seen.add(key) - columns.append( - CompiledLineageSourceFact( - resource_type=resource_type, - resource_name=resource_name, - column_name=column_name, - ) - ) - return tuple( - sorted( - columns, - key=lambda column: ( - column.resource_type.value, - column.resource_name, - column.column_name, - ), - ) - ), confidence - - -def _compact_transform_kind( - *, projection: dict[str, Any], has_upstream: bool -) -> ColumnTransformKind: - transform_kind: str = str(projection.get(_POLYGLOT_ANALYSIS_TRANSFORM_KIND) or "") - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_STAR: - return ColumnTransformKind.STAR - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_CAST: - return ColumnTransformKind.CAST - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_AGGREGATION: - return ColumnTransformKind.AGGREGATION - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_CONSTANT or not has_upstream: - return ColumnTransformKind.CONSTANT - if transform_kind == _POLYGLOT_ANALYSIS_TRANSFORM_DIRECT: - return ColumnTransformKind.DIRECT - return ColumnTransformKind.EXPRESSION diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/cte_facts.py b/src/sqlbuild/compiler/compile/_helpers/analysis/cte_facts.py deleted file mode 100644 index f9e4616fec..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/cte_facts.py +++ /dev/null @@ -1,1196 +0,0 @@ -"""Conservative CTE output fact recovery for SQL analysis.""" - -from __future__ import annotations - -from typing import Any, Protocol, cast - -from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.adapter.contract.types import TypeFamily -from sqlbuild.adapter.type_system.main.normalize_type import normalize_type -from sqlbuild.adapter.type_system.main.types_equal import types_equal -from sqlbuild.compiler.compile.constants import SQL_WILDCARD_TOKEN -from sqlbuild.compiler.compile.models import CteFactResolvers, NonNullFilterContext -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_COLUMN_USES as _POLYGLOT_ANALYSIS_COLUMN_USES, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_CONTEXT as _POLYGLOT_ANALYSIS_CONTEXT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_CONTEXT_FILTER as _POLYGLOT_ANALYSIS_CONTEXT_FILTER, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_ANALYSIS_EXPRESSION_SQL as _POLYGLOT_ANALYSIS_EXPRESSION_SQL, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_BINARY_OPERAND_COUNT as _POLYGLOT_BINARY_OPERAND_COUNT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_BOOLEAN_RESULT_KINDS as _POLYGLOT_BOOLEAN_RESULT_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_ALIAS as _POLYGLOT_KIND_ALIAS -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_AND as _POLYGLOT_KIND_AND -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_ANNOTATED as _POLYGLOT_KIND_ANNOTATED, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_CASE as _POLYGLOT_KIND_CASE -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_CAST as _POLYGLOT_KIND_CAST -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_COALESCE as _POLYGLOT_KIND_COALESCE, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_COLUMN as _POLYGLOT_KIND_COLUMN -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_CONCAT as _POLYGLOT_KIND_CONCAT -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_FUNCTION as _POLYGLOT_KIND_FUNCTION, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_IF_FUNC as _POLYGLOT_KIND_IF_FUNC, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_IS_NULL as _POLYGLOT_KIND_IS_NULL, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_LITERAL as _POLYGLOT_KIND_LITERAL -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_NULL as _POLYGLOT_KIND_NULL -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_PAREN as _POLYGLOT_KIND_PAREN -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_SELECT as _POLYGLOT_KIND_SELECT -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_KIND_SUBSTRING as _POLYGLOT_KIND_SUBSTRING, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_TABLE as _POLYGLOT_KIND_TABLE -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_LITERAL_TYPE_STRING as _POLYGLOT_LITERAL_TYPE_STRING, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_NULLIF_FUNCTION_NAME as _POLYGLOT_NULLIF_FUNCTION_NAME, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_COLUMN as _POLYGLOT_PAYLOAD_COLUMN, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_LEFT as _POLYGLOT_PAYLOAD_LEFT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_LITERAL_TYPE as _POLYGLOT_PAYLOAD_LITERAL_TYPE, -) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_PAYLOAD_NAME as _POLYGLOT_PAYLOAD_NAME -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_NOT as _POLYGLOT_PAYLOAD_NOT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_RIGHT as _POLYGLOT_PAYLOAD_RIGHT, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_TABLE as _POLYGLOT_PAYLOAD_TABLE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_THIS as _POLYGLOT_PAYLOAD_THIS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_PAYLOAD_WHERE_CLAUSE as _POLYGLOT_PAYLOAD_WHERE_CLAUSE, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_SET_OPERATION_KINDS as _POLYGLOT_SET_OPERATION_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import ( - POLYGLOT_TYPE_PASSTHROUGH_KINDS as _POLYGLOT_TYPE_PASSTHROUGH_KINDS, -) -from sqlbuild.compiler.sql_analysis.constants import SQL_NULL_KEYWORD as _SQL_NULL_KEYWORD - -_NULL_SET_OPERATION_TYPE: str = "__SQLBUILD_NULL_SET_OPERATION_TYPE__" - - -class _ExpressionTypeResolver(Protocol): - def __call__( - self, *, expression: Any, inference_profile: ExpressionInferenceProfile - ) -> str | None: ... - - -class _NullabilityResolver(Protocol): - def __call__( - self, - *, - expression: Any, - alias_nullability: dict[str, InferredNullability], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, - ) -> InferredNullability: ... - - -class _ShallowNullabilityResolver(Protocol): - def __call__( - self, *, expression: Any, inference_profile: ExpressionInferenceProfile - ) -> InferredNullability: ... - - -class _AliasNullabilityResolver(Protocol): - def __call__( - self, - *, - select: Any, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - ) -> dict[str, InferredNullability]: ... - - -def _polyglot_cte_passthrough_facts( - *, - polyglot_module: Any, - cleaned_sql: str, - dialect: str | None, - column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, - analysis: dict[str, Any], - resolvers: CteFactResolvers, -) -> tuple[dict[str, str], dict[str, InferredNullability], frozenset[str], Any | None]: - """Recover facts that compact analysis skips across direct CTE reads.""" - - if not analysis.get("cteFacts") or not (column_types_by_table or column_nullability_by_table): - return {}, {}, frozenset(), None - try: - parsed: Any = polyglot_module.parse_one(cleaned_sql, dialect=dialect or "generic") - except polyglot_module.PolyglotError: - return {}, {}, frozenset(), None - ctes: tuple[tuple[str, Any, bool], ...] = _polyglot_top_level_ctes(parsed) - if not ctes: - return {}, {}, frozenset(), parsed - inferred_types: dict[str, str] = ( - _polyglot_cte_passthrough_types_from_parsed( - parsed=parsed, - column_types_by_table=column_types_by_table, - inference_profile=inference_profile, - expression_type_resolver=resolvers.expression_type, - ) - if column_types_by_table - else {} - ) - inferred_nullability: dict[str, InferredNullability] = ( - _polyglot_cte_passthrough_nullability_from_parsed( - parsed=parsed, - column_nullability_by_table=column_nullability_by_table, - inference_profile=inference_profile, - nullability_resolver=resolvers.nullability, - shallow_nullability_resolver=resolvers.shallow_nullability, - alias_nullability_resolver=resolvers.alias_nullability, - ) - if column_nullability_by_table - else {} - ) - return ( - inferred_types, - inferred_nullability, - _polyglot_direct_cte_output_names( - parsed=parsed, - cte_names=frozenset(name for name, _, _ in ctes), - ), - parsed, - ) - - -def _polyglot_direct_cte_output_names(*, parsed: Any, cte_names: frozenset[str]) -> frozenset[str]: - normalized_cte_names: frozenset[str] = frozenset(name.casefold() for name in cte_names) - terminal_tables: tuple[Any, ...] = _polyglot_direct_select_tables(parsed) - if not any( - str(getattr(table, "name", "") or "").casefold() in normalized_cte_names - for table in terminal_tables - ): - return frozenset() - output_names: set[str] = set() - projection: Any - for projection in getattr(parsed, "expressions", ()): - projection = _unwrap_polyglot_annotations(projection) - expression: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - if str(getattr(expression, "kind", "")) != _POLYGLOT_KIND_COLUMN: - continue - output_name: str = str(getattr(projection, "output_name", "") or "") - if output_name and output_name != SQL_WILDCARD_TOKEN: - output_names.add(output_name) - return frozenset(output_names) - - -def _polyglot_cte_passthrough_types_from_parsed( - *, - parsed: Any, - column_types_by_table: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, - top_level_ctes: tuple[tuple[str, Any, bool], ...] | None = None, - referenced_table_names: tuple[str, ...] | None = None, -) -> dict[str, str]: - ctes: tuple[tuple[str, Any, bool], ...] = ( - _polyglot_top_level_ctes(parsed) if top_level_ctes is None else top_level_ctes - ) - if not ctes: - return {} - relation_types: dict[str, dict[str, str]] = _polyglot_referenced_relation_facts( - parsed=parsed, - facts_by_table=column_types_by_table, - table_names=referenced_table_names, - ) - cte_name: str - cte_body: Any - for cte_name, cte_body, has_column_aliases in ctes: - if has_column_aliases: - continue - inferred: dict[str, str] = _polyglot_select_output_types( - select=cte_body, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if inferred: - relation_types[cte_name] = inferred - if str(getattr(parsed, "kind", "")) in _POLYGLOT_SET_OPERATION_KINDS: - return _polyglot_set_operation_output_types( - operation=parsed, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - return _polyglot_direct_select_output_types( - select=parsed, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - - -def _polyglot_cte_passthrough_nullability_from_parsed( - *, - parsed: Any, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, - nullability_resolver: _NullabilityResolver, - shallow_nullability_resolver: _ShallowNullabilityResolver, - alias_nullability_resolver: _AliasNullabilityResolver, - top_level_ctes: tuple[tuple[str, Any, bool], ...] | None = None, - referenced_table_names: tuple[str, ...] | None = None, -) -> dict[str, InferredNullability]: - ctes: tuple[tuple[str, Any, bool], ...] = ( - _polyglot_top_level_ctes(parsed) if top_level_ctes is None else top_level_ctes - ) - if not ctes: - return {} - relation_nullability: dict[str, dict[str, InferredNullability]] = ( - _polyglot_referenced_relation_facts( - parsed=parsed, - facts_by_table=column_nullability_by_table, - table_names=referenced_table_names, - ) - ) - for cte_name, cte_body, has_column_aliases in ctes: - if has_column_aliases: - continue - inferred: dict[str, InferredNullability] = _polyglot_direct_select_output_nullability( - select=cte_body, - relation_nullability=relation_nullability, - inference_profile=inference_profile, - nullability_resolver=nullability_resolver, - shallow_nullability_resolver=shallow_nullability_resolver, - alias_nullability_resolver=alias_nullability_resolver, - ) - if inferred: - relation_nullability[cte_name] = inferred - return _polyglot_direct_select_output_nullability( - select=parsed, - relation_nullability=relation_nullability, - inference_profile=inference_profile, - nullability_resolver=nullability_resolver, - shallow_nullability_resolver=shallow_nullability_resolver, - alias_nullability_resolver=alias_nullability_resolver, - ) - - -def _polyglot_referenced_relation_facts[T]( - *, - parsed: Any, - facts_by_table: dict[str, dict[str, T]], - table_names: tuple[str, ...] | None = None, -) -> dict[str, dict[str, T]]: - referenced_facts: dict[str, dict[str, T]] = {} - names: tuple[str, ...] = ( - tuple( - str(getattr(table, "name", "") or "") for table in parsed.find_all(_POLYGLOT_KIND_TABLE) - ) - if table_names is None - else table_names - ) - for table_name in names: - column_facts: dict[str, T] | None = facts_by_table.get(table_name) - if column_facts is not None: - referenced_facts[table_name] = column_facts - return referenced_facts - - -def _polyglot_direct_select_output_nullability( - *, - select: Any, - relation_nullability: dict[str, dict[str, InferredNullability]], - inference_profile: ExpressionInferenceProfile, - nullability_resolver: _NullabilityResolver, - shallow_nullability_resolver: _ShallowNullabilityResolver, - alias_nullability_resolver: _AliasNullabilityResolver, -) -> dict[str, InferredNullability]: - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return {} - direct_table_names: tuple[str, ...] = tuple( - str(getattr(table, "name", "") or "") for table in _polyglot_direct_select_tables(select) - ) - scoped_nullability: dict[str, dict[str, InferredNullability]] = {} - for table_name in direct_table_names: - column_facts: dict[str, InferredNullability] | None = _case_insensitive_mapping_get( - mapping=relation_nullability, - key=table_name, - ) - if column_facts is not None: - scoped_nullability[table_name] = dict(column_facts) - has_known_nullability: bool = _has_known_nullability(scoped_nullability) - alias_nullability: dict[str, InferredNullability] = ( - alias_nullability_resolver( - select=select, - column_nullability_by_table=scoped_nullability, - ) - if has_known_nullability - else {} - ) - non_null_filter_context: NonNullFilterContext | None = _polyglot_non_null_filter_context( - select=select, - column_nullability_by_table=scoped_nullability, - ) - inferred: dict[str, InferredNullability] = {} - projection: Any - for raw_projection in getattr(select, "expressions", ()): - projection: Any = _unwrap_polyglot_annotations(raw_projection) - if bool(getattr(projection, "is_star", False)): - continue - output_name: str = str(getattr(projection, "output_name", "") or "") - if not output_name or output_name == SQL_WILDCARD_TOKEN: - continue - expression: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - nullability: InferredNullability = ( - InferredNullability.NON_NULL - if _polyglot_expression_is_non_null_after_filter( - expression=expression, - context=non_null_filter_context, - ) - else ( - nullability_resolver( - expression=expression, - alias_nullability=alias_nullability, - column_nullability_by_table=scoped_nullability, - inference_profile=inference_profile, - ) - if has_known_nullability - else shallow_nullability_resolver( - expression=expression, - inference_profile=inference_profile, - ) - ) - ) - if nullability != InferredNullability.UNKNOWN: - inferred[output_name] = nullability - return inferred - - -def _polyglot_filtered_non_null_outputs( - *, - polyglot_module: Any, - cleaned_sql: str, - dialect: str | None, - analysis: dict[str, Any], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - parsed: Any | None = None, -) -> frozenset[str]: - column_uses: object = analysis.get(_POLYGLOT_ANALYSIS_COLUMN_USES) - if not isinstance(column_uses, list) or not any( - isinstance(value, dict) - and value.get(_POLYGLOT_ANALYSIS_CONTEXT) == _POLYGLOT_ANALYSIS_CONTEXT_FILTER - and _SQL_NULL_KEYWORD in str(value.get(_POLYGLOT_ANALYSIS_EXPRESSION_SQL) or "").upper() - for value in column_uses - ): - return frozenset() - if parsed is None: - try: - parsed = polyglot_module.parse_one( - cleaned_sql, - dialect=dialect or "generic", - ) - except polyglot_module.PolyglotError: - return frozenset() - select: Any | None = parsed - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - select = parsed.find(_POLYGLOT_KIND_SELECT) - if select is None or str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return frozenset() - outputs: set[str] = set() - context: NonNullFilterContext | None = _polyglot_non_null_filter_context( - select=select, - column_nullability_by_table=column_nullability_by_table, - ) - for raw_projection in getattr(select, "expressions", ()): - projection: Any = _unwrap_polyglot_annotations(raw_projection) - output_name: str = str(getattr(projection, "output_name", "") or "") - if not output_name or output_name == SQL_WILDCARD_TOKEN: - continue - expression: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - if _polyglot_expression_is_non_null_after_filter( - expression=expression, - context=context, - ): - outputs.add(output_name) - return frozenset(outputs) - - -def _polyglot_expression_is_non_null_after_filter( - *, - expression: Any, - context: NonNullFilterContext | None, -) -> bool: - if context is None: - return False - expression_reference: tuple[str, str] | None = _polyglot_direct_column_reference(expression) - if expression_reference is None: - return False - resolved_expression: tuple[str, str] | None = _polyglot_resolved_relation_column( - reference=expression_reference, - relations=context.relations, - ) - return resolved_expression is not None and resolved_expression in context.columns - - -def _polyglot_non_null_filter_context( - *, - select: Any, - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> NonNullFilterContext | None: - where_payload: object = select.arg(_POLYGLOT_PAYLOAD_WHERE_CLAUSE) - if not isinstance(where_payload, dict): - return None - predicate: object = cast(dict[str, object], where_payload).get(_POLYGLOT_PAYLOAD_THIS) - non_null_references: tuple[tuple[str, str], ...] = _polyglot_non_null_conjuncts(predicate) - if not non_null_references: - return None - relations: list[tuple[str, dict[str, InferredNullability]]] = [] - for table in _polyglot_direct_select_tables(select): - table_name: str = str(getattr(table, "name", "") or "") - alias: str = str(getattr(table, "alias_or_name", "") or "") - columns: dict[str, InferredNullability] | None = _case_insensitive_mapping_get( - mapping=column_nullability_by_table, - key=table_name, - ) - if columns is not None: - relations.append((alias or table_name, columns)) - relation_tuple: tuple[tuple[str, dict[str, InferredNullability]], ...] = tuple(relations) - columns: frozenset[tuple[str, str]] = frozenset( - resolved - for reference in non_null_references - if ( - resolved := _polyglot_resolved_relation_column( - reference=reference, - relations=relation_tuple, - ) - ) - ) - return NonNullFilterContext(relations=relation_tuple, columns=columns) if columns else None - - -def _polyglot_direct_column_reference(expression: Any) -> tuple[str, str] | None: - expression = _unwrap_polyglot_annotations(expression) - if str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_CAST: - expression = _unwrap_polyglot_annotations(getattr(expression, "this", None)) - if str(getattr(expression, "kind", "")) != _POLYGLOT_KIND_COLUMN: - return None - column_name: str = str(getattr(expression, "name", "") or "") - if not column_name: - return None - expression_args: object = getattr(expression, "args", None) - table_name: str = "" - if isinstance(expression_args, dict): - table_name = _polyglot_name_payload_value( - cast(dict[str, object], expression_args).get(_POLYGLOT_PAYLOAD_TABLE) - ) - return table_name, column_name - - -def _polyglot_resolved_relation_column( - *, - reference: tuple[str, str], - relations: tuple[tuple[str, dict[str, InferredNullability]], ...], -) -> tuple[str, str] | None: - table_name, column_name = reference - matching: list[tuple[str, str]] = [] - for alias, columns in relations: - if table_name and alias.casefold() != table_name.casefold(): - continue - column_key: str | None = _case_insensitive_mapping_key( - mapping=columns, - key=column_name, - ) - if column_key is not None: - matching.append((alias.casefold(), column_key.casefold())) - return matching[0] if len(matching) == 1 else None - - -def _polyglot_non_null_conjuncts(payload: object) -> tuple[tuple[str, str], ...]: - if not isinstance(payload, dict): - return () - payload_dict: dict[str, object] = cast(dict[str, object], payload) - paren_payload: object = payload_dict.get(_POLYGLOT_KIND_PAREN) - if isinstance(paren_payload, dict): - paren_payload_dict: dict[str, object] = cast(dict[str, object], paren_payload) - return _polyglot_non_null_conjuncts(paren_payload_dict.get(_POLYGLOT_PAYLOAD_THIS)) - and_payload: object = payload_dict.get(_POLYGLOT_KIND_AND) - if isinstance(and_payload, dict): - and_payload_dict: dict[str, object] = cast(dict[str, object], and_payload) - return ( - *_polyglot_non_null_conjuncts(and_payload_dict.get(_POLYGLOT_PAYLOAD_LEFT)), - *_polyglot_non_null_conjuncts(and_payload_dict.get(_POLYGLOT_PAYLOAD_RIGHT)), - ) - is_null_payload: object = payload_dict.get(_POLYGLOT_KIND_IS_NULL) - is_null_payload_dict: dict[str, object] = ( - cast(dict[str, object], is_null_payload) if isinstance(is_null_payload, dict) else {} - ) - if not is_null_payload_dict or is_null_payload_dict.get(_POLYGLOT_PAYLOAD_NOT) is not True: - return () - expression_payload: object = is_null_payload_dict.get(_POLYGLOT_PAYLOAD_THIS) - if not isinstance(expression_payload, dict): - return () - expression_payload_dict: dict[str, object] = cast(dict[str, object], expression_payload) - column_payload: object = expression_payload_dict.get(_POLYGLOT_PAYLOAD_COLUMN) - if not isinstance(column_payload, dict): - return () - column_payload_dict: dict[str, object] = cast(dict[str, object], column_payload) - column_name: str = _polyglot_name_payload_value(column_payload_dict.get(_POLYGLOT_PAYLOAD_NAME)) - table_name: str = _polyglot_name_payload_value(column_payload_dict.get(_POLYGLOT_PAYLOAD_TABLE)) - return ((table_name, column_name),) if column_name else () - - -def _polyglot_star_output_types( - *, projection: Any, types_by_alias: dict[str, dict[str, str]] -) -> dict[str, str]: - if getattr(projection, "args", {}).get("rename"): - return {} - table_payload: object = getattr(projection, "args", {}).get("table") - table_name: str = _polyglot_name_payload_value(table_payload) - source_types: list[dict[str, str]] = [] - if table_name: - column_types: dict[str, str] | None = _case_insensitive_mapping_get( - mapping=types_by_alias, - key=table_name, - ) - if column_types is not None: - source_types.append(column_types) - else: - seen_relations: set[int] = set() - for column_types in types_by_alias.values(): - if id(column_types) in seen_relations: - continue - seen_relations.add(id(column_types)) - source_types.append(column_types) - excluded_names: frozenset[str] = frozenset( - name.casefold() - for value in (getattr(projection, "args", {}).get("except") or ()) - if (name := _polyglot_name_payload_value(value)) - ) - replaced_names: frozenset[str] = frozenset( - name.casefold() - for value in (getattr(projection, "args", {}).get("replace") or ()) - if isinstance(value, dict) - and (name := _polyglot_name_payload_value(cast(dict[str, object], value).get("alias"))) - ) - inferred: dict[str, str] = {} - ambiguous_names: set[str] = set() - for column_types in source_types: - for column_name, column_type in column_types.items(): - normalized_name: str = column_name.casefold() - if normalized_name in excluded_names or normalized_name in replaced_names: - continue - existing_name: str | None = next( - (name for name in inferred if name.casefold() == normalized_name), - None, - ) - if existing_name is not None: - ambiguous_names.add(existing_name) - continue - inferred[column_name] = column_type - for ambiguous_name in ambiguous_names: - inferred.pop(ambiguous_name, None) - return inferred - - -def _polyglot_top_level_ctes(root: Any) -> tuple[tuple[str, Any, bool], ...]: - targeted_ctes: object = getattr(root, "with_ctes", None) - if callable(targeted_ctes): - values: object = targeted_ctes() - if isinstance(values, list): - return tuple( - (str(name), body, bool(has_column_alias)) for name, has_column_alias, body in values - ) - with_payload: object = root.arg("with") - raw_ctes: object = ( - cast(dict[str, object], with_payload).get("ctes") - if isinstance(with_payload, dict) - else None - ) - if not isinstance(raw_ctes, list): - return () - children: tuple[Any, ...] = tuple(root.children()) - if len(children) < len(raw_ctes): - return () - cte_bodies: tuple[Any, ...] = children[-len(raw_ctes) :] - ctes: list[tuple[str, Any, bool]] = [] - for raw_cte, body in zip(raw_ctes, cte_bodies, strict=True): - if not isinstance(raw_cte, dict): - return () - cte_payload: dict[str, object] = cast(dict[str, object], raw_cte) - cte_name: str = _polyglot_name_payload_value(cte_payload.get("alias")) - if not cte_name: - return () - columns: object = cte_payload.get("columns") - ctes.append((cte_name, body, isinstance(columns, list) and bool(columns))) - return tuple(ctes) - - -def _polyglot_select_output_types( - *, - select: Any, - relation_types: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> dict[str, str]: - select = _unwrap_polyglot_annotations(select) - if _polyglot_top_level_ctes(select): - return _polyglot_cte_passthrough_types_from_parsed( - parsed=select, - column_types_by_table=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if str(getattr(select, "kind", "")) in _POLYGLOT_SET_OPERATION_KINDS: - return _polyglot_set_operation_output_types( - operation=select, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - return _polyglot_direct_select_output_types( - select=select, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - - -def _polyglot_set_operation_output_types( - *, - operation: Any, - relation_types: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> dict[str, str]: - if bool(operation.arg("by_name")): - branch_types: list[dict[str, str]] = [] - for side in ("left", "right"): - branch: Any = operation.arg(side) - if branch is None: - return {} - branch_types.append( - _polyglot_select_output_types( - select=branch, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - ) - left_types, right_types = branch_types - return { - name: common_type - for name, left_type in left_types.items() - if ( - common_type := _polyglot_common_set_operation_type( - left=left_type, - right=_case_insensitive_mapping_get(mapping=right_types, key=name), - inference_profile=inference_profile, - ) - ) - is not None - } - branch_slots: list[tuple[tuple[str, str | None], ...]] = [] - for side in ("left", "right"): - branch: Any = operation.arg(side) - if branch is None: - return {} - inferred: tuple[tuple[str, str | None], ...] = _polyglot_select_output_type_slots( - select=branch, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if not inferred: - return {} - branch_slots.append(inferred) - left_slots, right_slots = branch_slots - if len(left_slots) != len(right_slots): - return {} - return { - left_name: common_type - for (left_name, left_type), (_, right_type) in zip( - left_slots, - right_slots, - strict=True, - ) - if ( - common_type := _polyglot_common_set_operation_type( - left=left_type, - right=right_type, - inference_profile=inference_profile, - ) - ) - is not None - } - - -def _polyglot_select_output_type_slots( - *, - select: Any, - relation_types: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> tuple[tuple[str, str | None], ...]: - select = _unwrap_polyglot_annotations(select) - if str(getattr(select, "kind", "")) in _POLYGLOT_SET_OPERATION_KINDS: - branch_slots: list[tuple[tuple[str, str | None], ...]] = [] - for side in ("left", "right"): - branch: Any = select.arg(side) - if branch is None: - return () - slots: tuple[tuple[str, str | None], ...] = _polyglot_select_output_type_slots( - select=branch, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if not slots: - return () - branch_slots.append(slots) - left_slots, right_slots = branch_slots - if bool(select.arg("by_name")): - right_types: dict[str, str | None] = dict(right_slots) - return tuple( - ( - name, - _polyglot_common_set_operation_type( - left=left_type, - right=_case_insensitive_mapping_get(mapping=right_types, key=name), - inference_profile=inference_profile, - ), - ) - for name, left_type in left_slots - ) - if len(left_slots) != len(right_slots): - return () - return tuple( - ( - left_name, - _polyglot_common_set_operation_type( - left=left_type, - right=right_type, - inference_profile=inference_profile, - ), - ) - for (left_name, left_type), (_, right_type) in zip( - left_slots, - right_slots, - strict=True, - ) - ) - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return () - inferred: dict[str, str] = _polyglot_direct_select_output_types( - select=select, - relation_types=relation_types, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - slots: list[tuple[str, str | None]] = [] - for raw_projection in getattr(select, "expressions", ()): - projection: Any = _unwrap_polyglot_annotations(raw_projection) - if bool(getattr(projection, "is_star", False)): - return () - output_name: str = str(getattr(projection, "output_name", "") or "") - if not output_name or output_name == SQL_WILDCARD_TOKEN: - return () - inferred_type: str | None = _case_insensitive_mapping_get( - mapping=inferred, - key=output_name, - ) - if inferred_type is None: - expression: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - if str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_NULL: - inferred_type = _NULL_SET_OPERATION_TYPE - slots.append((output_name, inferred_type)) - return tuple(slots) - - -def _polyglot_common_set_operation_type( - *, - left: str | None, - right: str | None, - inference_profile: ExpressionInferenceProfile, -) -> str | None: - if left == _NULL_SET_OPERATION_TYPE and right == _NULL_SET_OPERATION_TYPE: - return None - if left == _NULL_SET_OPERATION_TYPE: - return right - if right == _NULL_SET_OPERATION_TYPE: - return left - if left is None or right is None: - return None - return ( - left - if _polyglot_types_equal( - left=left, - right=right, - inference_profile=inference_profile, - ) - else None - ) - - -def _polyglot_types_equal( - *, - left: str, - right: str, - inference_profile: ExpressionInferenceProfile, -) -> bool: - return left == right or types_equal( - left=left, - right=right, - dialect=inference_profile.sql_analysis_dialect, - ) - - -def _polyglot_direct_select_output_types( - *, - select: Any, - relation_types: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> dict[str, str]: - if str(getattr(select, "kind", "")) != _POLYGLOT_KIND_SELECT: - return {} - types_by_alias: dict[str, dict[str, str]] = {} - for table in _polyglot_direct_select_tables(select): - table_name: str = str(getattr(table, "name", "") or "") - column_types: dict[str, str] | None = _case_insensitive_mapping_get( - mapping=relation_types, - key=table_name, - ) - if column_types is None: - continue - types_by_alias[table_name] = column_types - alias_or_name: str = str(getattr(table, "alias_or_name", "") or "") - if alias_or_name: - types_by_alias[alias_or_name] = column_types - inferred: dict[str, str] = {} - projection: Any - for projection in getattr(select, "expressions", ()): - projection = _unwrap_polyglot_annotations(projection) - if bool(getattr(projection, "is_star", False)): - inferred.update( - _polyglot_star_output_types( - projection=projection, - types_by_alias=types_by_alias, - ) - ) - continue - output_name: str = str(getattr(projection, "output_name", "") or "") - if not output_name or output_name == SQL_WILDCARD_TOKEN: - continue - expression: Any = ( - projection.this - if str(getattr(projection, "kind", "")) == _POLYGLOT_KIND_ALIAS - else projection - ) - inferred_type: str | None = _polyglot_direct_expression_type( - expression=expression, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if inferred_type is not None: - inferred[output_name] = inferred_type - return inferred - - -def _polyglot_direct_expression_type( - *, - expression: Any, - types_by_alias: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> str | None: - expression = _unwrap_polyglot_annotations(expression) - kind: str = str(getattr(expression, "kind", "")) - inferred_type: str | None = expression_type_resolver( - expression=expression, - inference_profile=inference_profile, - ) - if inferred_type is not None: - return inferred_type - if kind == _POLYGLOT_KIND_COLUMN: - return _polyglot_direct_column_type( - expression=expression, - types_by_alias=types_by_alias, - ) - if kind in _POLYGLOT_BOOLEAN_RESULT_KINDS: - return "BOOLEAN" - if kind == _POLYGLOT_KIND_CONCAT: - operands: tuple[Any, ...] = tuple( - operand - for key in ("left", "right") - if (operand := getattr(expression, "args", {}).get(key)) is not None - ) - if len(operands) != _POLYGLOT_BINARY_OPERAND_COUNT: - return None - operand_types: list[str] = [] - for operand in operands: - if ( - str(getattr(operand, "kind", "")) == _POLYGLOT_KIND_LITERAL - and getattr(operand, "args", {}).get(_POLYGLOT_PAYLOAD_LITERAL_TYPE) - == _POLYGLOT_LITERAL_TYPE_STRING - ): - operand_types.append("TEXT") - continue - operand_type: str | None = _polyglot_direct_expression_type( - expression=operand, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if operand_type is None: - return None - operand_types.append(operand_type) - return ( - "TEXT" - if all( - normalize_type( - type_sql=operand_type, - dialect=inference_profile.sql_analysis_dialect, - ).family - == TypeFamily.STRING - for operand_type in operand_types - ) - else None - ) - if kind == _POLYGLOT_KIND_SUBSTRING: - inner: Any | None = getattr(expression, "args", {}).get("this") - return ( - _polyglot_direct_expression_type( - expression=inner, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if inner is not None - else None - ) - if kind in _POLYGLOT_TYPE_PASSTHROUGH_KINDS: - inner: Any | None = getattr(expression, "this", None) or getattr( - expression, "args", {} - ).get("this") - return ( - _polyglot_direct_expression_type( - expression=inner, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if inner is not None - else None - ) - result_expressions: tuple[Any, ...] = _polyglot_result_expressions(expression) - if result_expressions: - return _polyglot_common_result_type( - expressions=result_expressions, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - return None - - -def _polyglot_result_expressions(expression: Any) -> tuple[Any, ...]: - kind: str = str(getattr(expression, "kind", "")) - args: dict[str, Any] = getattr(expression, "args", {}) - if kind == _POLYGLOT_KIND_COALESCE: - values: object = args.get("expressions") - return tuple(values) if isinstance(values, list) else () - if kind == _POLYGLOT_KIND_IF_FUNC: - return tuple( - value for key in ("true_value", "false_value") if (value := args.get(key)) is not None - ) - if kind == _POLYGLOT_KIND_CASE: - values: list[Any] = [] - whens: object = args.get("whens") - children: tuple[Any, ...] = tuple(expression.children()) - if isinstance(whens, list): - first_condition_index: int = 1 if args.get("operand") is not None else 0 - values.extend( - children[first_condition_index + (index * 2) + 1] for index in range(len(whens)) - ) - if args.get("else_") is not None and children: - values.append(children[-1]) - return tuple(values) - if ( - kind == _POLYGLOT_KIND_FUNCTION - and str(getattr(expression, "name", "")).upper() == _POLYGLOT_NULLIF_FUNCTION_NAME - ): - values = args.get("args") - return (values[0],) if isinstance(values, list) and values else () - return () - - -def _polyglot_common_result_type( - *, - expressions: tuple[Any, ...], - types_by_alias: dict[str, dict[str, str]], - inference_profile: ExpressionInferenceProfile, - expression_type_resolver: _ExpressionTypeResolver, -) -> str | None: - resolved_types: list[str] = [] - for raw_expression in expressions: - expression: Any = _unwrap_polyglot_annotations(raw_expression) - if str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_NULL: - continue - resolved_type: str | None = _polyglot_direct_expression_type( - expression=expression, - types_by_alias=types_by_alias, - inference_profile=inference_profile, - expression_type_resolver=expression_type_resolver, - ) - if resolved_type is None: - return None - resolved_types.append(resolved_type) - if not resolved_types: - return None - first_type: str = resolved_types[0] - if all( - _polyglot_types_equal( - left=first_type, - right=other_type, - inference_profile=inference_profile, - ) - for other_type in resolved_types[1:] - ): - return first_type - return None - - -def _polyglot_direct_select_tables(select: Any) -> tuple[Any, ...]: - return tuple( - child - for child in select.children() - if str(getattr(child, "kind", "")) == _POLYGLOT_KIND_TABLE - ) - - -def _polyglot_direct_column_type( - *, expression: Any, types_by_alias: dict[str, dict[str, str]] -) -> str | None: - column_name: str = str(getattr(expression, "name", "") or "") - table_name: str = _polyglot_column_table_name(expression) - if table_name: - column_types: dict[str, str] | None = _case_insensitive_mapping_get( - mapping=types_by_alias, - key=table_name, - ) - return ( - _case_insensitive_mapping_get(mapping=column_types, key=column_name) - if column_types is not None - else None - ) - matching_types: list[str] = [] - seen_relations: set[int] = set() - for column_types in types_by_alias.values(): - if id(column_types) in seen_relations: - continue - seen_relations.add(id(column_types)) - inferred_type: str | None = _case_insensitive_mapping_get( - mapping=column_types, - key=column_name, - ) - if inferred_type is not None: - matching_types.append(inferred_type) - return matching_types[0] if len(matching_types) == 1 else None - - -def _case_insensitive_mapping_get[T](*, mapping: dict[str, T], key: str) -> T | None: - direct: T | None = mapping.get(key) - if direct is not None: - return direct - normalized_key: str = key.casefold() - matches: list[T] = [ - value for candidate, value in mapping.items() if candidate.casefold() == normalized_key - ] - return matches[0] if len(matches) == 1 else None - - -def _case_insensitive_mapping_key[T](*, mapping: dict[str, T], key: str) -> str | None: - if key in mapping: - return key - normalized_key: str = key.casefold() - matches: list[str] = [ - candidate for candidate in mapping if candidate.casefold() == normalized_key - ] - return matches[0] if len(matches) == 1 else None - - -def _has_known_nullability( - column_nullability_by_table: dict[str, dict[str, InferredNullability]], -) -> bool: - for column_facts in column_nullability_by_table.values(): - if any(value != InferredNullability.UNKNOWN for value in column_facts.values()): - return True - return False - - -def _unwrap_polyglot_annotations(expression: Any) -> Any: - while str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_ANNOTATED: - expression = expression.this - return expression - - -def _polyglot_column_table_name(column: Any) -> str: - payload: object = column.to_dict().get(_POLYGLOT_PAYLOAD_COLUMN, {}) - if not isinstance(payload, dict): - return "" - table_payload: object = payload.get(_POLYGLOT_PAYLOAD_TABLE) - if not isinstance(table_payload, dict): - return "" - return _polyglot_name_payload_value(table_payload) - - -def _polyglot_name_payload_value(payload: object) -> str: - if isinstance(payload, str): - return payload - if not isinstance(payload, dict): - return "" - name: object = cast(dict[str, object], payload).get(_POLYGLOT_PAYLOAD_NAME) - return name if isinstance(name, str) else "" diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/cte_scopes.py b/src/sqlbuild/compiler/compile/_helpers/analysis/cte_scopes.py new file mode 100644 index 0000000000..40f141cb09 --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/cte_scopes.py @@ -0,0 +1,67 @@ +"""Top-level CTEs and direct tables of Polyglot wheel trees, for resolved column reads.""" + +from __future__ import annotations + +from typing import Any, cast + +from sqlbuild.compiler.sql_analysis.constants import ( + POLYGLOT_KIND_ANNOTATED as _POLYGLOT_KIND_ANNOTATED, +) +from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_TABLE as _POLYGLOT_KIND_TABLE +from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_PAYLOAD_NAME as _POLYGLOT_PAYLOAD_NAME + + +def _polyglot_top_level_ctes(root: Any) -> tuple[tuple[str, Any, bool], ...]: + targeted_ctes: object = getattr(root, "with_ctes", None) + if callable(targeted_ctes): + values: object = targeted_ctes() + if isinstance(values, list): + return tuple( + (str(name), body, bool(has_column_alias)) for name, has_column_alias, body in values + ) + with_payload: object = root.arg("with") + raw_ctes: object = ( + cast(dict[str, object], with_payload).get("ctes") + if isinstance(with_payload, dict) + else None + ) + if not isinstance(raw_ctes, list): + return () + children: tuple[Any, ...] = tuple(root.children()) + if len(children) < len(raw_ctes): + return () + cte_bodies: tuple[Any, ...] = children[-len(raw_ctes) :] + ctes: list[tuple[str, Any, bool]] = [] + for raw_cte, body in zip(raw_ctes, cte_bodies, strict=True): + if not isinstance(raw_cte, dict): + return () + cte_payload: dict[str, object] = cast(dict[str, object], raw_cte) + cte_name: str = _polyglot_name_payload_value(cte_payload.get("alias")) + if not cte_name: + return () + columns: object = cte_payload.get("columns") + ctes.append((cte_name, body, isinstance(columns, list) and bool(columns))) + return tuple(ctes) + + +def _polyglot_direct_select_tables(select: Any) -> tuple[Any, ...]: + return tuple( + child + for child in select.children() + if str(getattr(child, "kind", "")) == _POLYGLOT_KIND_TABLE + ) + + +def _unwrap_polyglot_annotations(expression: Any) -> Any: + while str(getattr(expression, "kind", "")) == _POLYGLOT_KIND_ANNOTATED: + expression = expression.this + return expression + + +def _polyglot_name_payload_value(payload: object) -> str: + if isinstance(payload, str): + return payload + if not isinstance(payload, dict): + return "" + name: object = cast(dict[str, object], payload).get(_POLYGLOT_PAYLOAD_NAME) + return name if isinstance(name, str) else "" diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py b/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py index 77f1c6df1b..6a6cfe07a4 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py @@ -5,9 +5,10 @@ from typing import Any, cast from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, ) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns from sqlbuild.compiler.compile.constants import ( POLYGLOT_ARRAY_KIND, POLYGLOT_FUNCTION_KIND, @@ -17,7 +18,6 @@ from sqlbuild.compiler.compile.models import ( FixtureColumnInference, InferredColumn, - PolyglotAnalysisResult, ) from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_NULL from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql @@ -28,11 +28,11 @@ def infer_fixture_column_facts( ) -> FixtureColumnInference | None: """Infer fixture columns and identify explicitly untyped NULL projections.""" - analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=query_sql, - inference_profile=inference_profile, - ) - if not analysis.analysis_succeeded or analysis.has_star or analysis.columns is None: + analysis: NativeQueryColumns = infer_native_query_columns( + queries=(NativeColumnQuery(sql=query_sql, mode="legacy", recover_cte_facts=False),), + profile=inference_profile, + )[0] + if not analysis.succeeded or analysis.has_star or analysis.columns is None: return None simple_types, null_literal_names, quoted_names = _simple_projection_facts( query_sql=query_sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py b/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py index 9ed2ea2cad..33861ab3aa 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py @@ -4,7 +4,9 @@ from collections.abc import Mapping -from sqlbuild.compiler.compile._helpers.analysis.columns import substitute_placeholder_defaults +from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( + substitute_placeholder_defaults, +) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_placeholders from sqlbuild.compiler.compile._helpers.render.cursor_intrinsics import ( cursor_intrinsics_analysis_sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py new file mode 100644 index 0000000000..46afcba52b --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py @@ -0,0 +1,131 @@ +"""Reference analysis names, lineage resource types, qualified-reference scans and placeholders.""" + +from __future__ import annotations + +import re +from collections.abc import Iterable + +from sqlbuild.compiler.compile.constants import ( + SQL_QUALIFIER_SEPARATOR_TOKEN, +) +from sqlbuild.compiler.compile.models import ( + CompileSqlReference, +) +from sqlbuild.compiler.compile.types import CompiledResourceType +from sqlbuild.compiler.references.types import SqlReferenceKind +from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql + +_PLACEHOLDER_PATTERN: re.Pattern[str] = re.compile(r"@@@(\w+)") +_QUALIFIED_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile( + r'(?[^"]+)"|`(?P[^`]+)`|' + r"\[(?P[^\]]+)\]|(?P[A-Za-z_$][A-Za-z0-9_$]*))" + r"(?:\s|--[^\r\n]*(?:\r?\n|$)|/\*.*?\*/)*\.", + re.DOTALL, +) +_QUALIFIED_SCAN_CONSUMING_TOKENS: tuple[str, ...] = ('"', "`", "[", "--", "/*") +_PLAIN_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile(r"[a-z_$][a-z0-9_$]*") +_IDENTIFIER_CHARACTERS: frozenset[str] = frozenset( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$" +) + + +def _lineage_resource_type(reference: CompileSqlReference) -> CompiledResourceType | None: + if reference.ref_kind == SqlReferenceKind.REF: + return CompiledResourceType.MODEL + if reference.ref_kind == SqlReferenceKind.SOURCE: + return CompiledResourceType.SOURCE + if reference.ref_kind == SqlReferenceKind.SEED: + return CompiledResourceType.SEED + if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: + return CompiledResourceType.TABLE_FN + return None + + +def _analysis_reference_name(reference: CompileSqlReference) -> str: + if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: + return table_function_analysis_name(reference.ref_name) + return reference.ref_name + + +def table_function_analysis_name(function_name: str) -> str: + """Return the stable relation stub used to analyze a table-function call.""" + + return f"__sqlbuild_table_function_{function_name}" + + +def substitute_placeholder_defaults(*, query_sql: str, placeholders: dict[str, str]) -> str: + """Replace @@@name tokens with their default values for SQL analysis parsing.""" + + if not placeholders: + return query_sql + + def _replacer(match: re.Match[str]) -> str: + name: str = match.group(1) + return placeholders.get(name, match.group(0)) + + return _PLACEHOLDER_PATTERN.sub(_replacer, query_sql) + + +def _replace_refs_with_stubs( + *, + query_sql: str, + dialect: str | None = None, + relation_stubs: dict[str, str] | None = None, +) -> str: + """Replace SQLBuild marker calls with parseable SQL stubs.""" + + return normalize_analysis_sql(sql=query_sql, dialect=dialect, stubs=relation_stubs) + + +def _qualified_reference_names(*, query_sql: str, reference_names: Iterable[str]) -> frozenset[str]: + """Return qualifier reference names, searching names directly when no token can hide one.""" + + if SQL_QUALIFIER_SEPARATOR_TOKEN not in query_sql: + return frozenset() + names_by_normalized: dict[str, list[str]] = {} + for value in reference_names: + name: str = str(value) + names_by_normalized.setdefault(name.casefold(), []).append(name) + if not names_by_normalized: + return frozenset() + if not query_sql.isascii() or any( + token in query_sql for token in _QUALIFIED_SCAN_CONSUMING_TOKENS + ): + return _scanned_qualified_reference_names( + query_sql=query_sql, names_by_normalized=names_by_normalized + ) + + folded_sql: str = query_sql.lower() + qualified: set[str] = set() + for normalized, names in names_by_normalized.items(): + if _PLAIN_IDENTIFIER_PATTERN.fullmatch(normalized) is not None and _is_qualifier( + folded_sql=folded_sql, name=normalized + ): + qualified.update(names) + return frozenset(qualified) + + +def _is_qualifier(*, folded_sql: str, name: str) -> bool: + length: int = len(folded_sql) + start: int = folded_sql.find(name) + while start != -1: + end: int = start + len(name) + if (start == 0 or folded_sql[start - 1] not in _IDENTIFIER_CHARACTERS) and ( + end == length or folded_sql[end] not in _IDENTIFIER_CHARACTERS + ): + while end < length and folded_sql[end].isspace(): + end += 1 + if end < length and folded_sql[end] == SQL_QUALIFIER_SEPARATOR_TOKEN: + return True + start = folded_sql.find(name, start + 1) + return False + + +def _scanned_qualified_reference_names( + *, query_sql: str, names_by_normalized: dict[str, list[str]] +) -> frozenset[str]: + qualified: set[str] = set() + for match in _QUALIFIED_IDENTIFIER_PATTERN.finditer(query_sql): + identifier: str = next(value for value in match.groups() if value is not None) + qualified.update(names_by_normalized.get(identifier.casefold(), ())) + return frozenset(qualified) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/required_columns.py b/src/sqlbuild/compiler/compile/_helpers/analysis/required_columns.py index 32c7deb5b4..d137543a4b 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/required_columns.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/required_columns.py @@ -5,16 +5,16 @@ from dataclasses import dataclass from typing import Any, cast -from sqlbuild.compiler.compile._helpers.analysis.columns import ( - _analysis_reference_name, - _lineage_resource_type, - _replace_refs_with_stubs, -) -from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( +from sqlbuild.compiler.compile._helpers.analysis.cte_scopes import ( _polyglot_direct_select_tables, _polyglot_top_level_ctes, _unwrap_polyglot_annotations, ) +from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( + _analysis_reference_name, + _lineage_resource_type, + _replace_refs_with_stubs, +) from sqlbuild.compiler.compile.models import CompiledLineageSourceFact, CompileSqlReference from sqlbuild.compiler.compile.types import CompiledResourceType from sqlbuild.compiler.sql_analysis.constants import ( diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py index 4de42529d1..1d713d446d 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py @@ -6,10 +6,10 @@ from functools import lru_cache from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, - analyze_queries_with_compact_polyglot_batch, +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, ) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns from sqlbuild.compiler.compile._helpers.assembly.native_declarations import ( known_declared_types, known_function_names, @@ -19,8 +19,6 @@ CompiledModelSqlTestPayload, CompiledProject, CompilerDiagnostic, - NativeCompactAnalysis, - PolyglotAnalysisResult, ) from sqlbuild.compiler.compile.types import ( CompiledResourceType, @@ -199,25 +197,16 @@ def _sql_test_columns( queries: tuple[str, ...] = tuple(bodies) if not queries: return {} - prepared: tuple[NativeCompactAnalysis, ...] = analyze_queries_with_compact_polyglot_batch( - query_sqls=queries, - references=((),) * len(queries), - placeholders=(None,) * len(queries), - column_nullability_by_table={}, - column_types_by_table={}, - inference_profile=profile, - recover_cte_facts=(False,) * len(queries), + analyses: tuple[NativeQueryColumns, ...] = infer_native_query_columns( + queries=tuple( + NativeColumnQuery(sql=sql, mode="batch", recover_cte_facts=False) for sql in queries + ), + profile=profile, ) - result: dict[str, tuple[str, ...]] = {} - for sql, native in zip(queries, prepared, strict=True): - analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=sql, - references=(), - inference_profile=profile, - allow_compact_analysis=True, - precomputed=native, - ) - result[sql] = tuple(column.name for column in analysis.columns or ()) + result: dict[str, tuple[str, ...]] = { + sql: tuple(column.name for column in analysis.columns or ()) + for sql, analysis in zip(queries, analyses, strict=True) + } return result diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 33b2721369..24d4ba7a47 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -7,16 +7,18 @@ from pathlib import Path from typing import Any, cast +import sqlbuild._native as _native from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.contract.types import BuiltinAdapter -from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest -from sqlbuild.compiler.compile._helpers.analysis.cache import ( - build_analysis_cache_context, +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, ) -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeModelAnalysisRequest +from sqlbuild.compiler.compile._helpers.analysis.binding_requests import ( get_complete_schema_binding_request, - infer_columns_with_sql_analysis, +) +from sqlbuild.compiler.compile._helpers.analysis.cache import ( + build_analysis_cache_context, ) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import ( model_placeholders as _model_placeholders, @@ -145,6 +147,7 @@ from sqlbuild.compiler.sql_analysis.constants import ( BINDING_UNKNOWN_TABLE_INTERNAL_CODE, NATIVE_DIALECT_ALIASES, + NATIVE_FAILURE_PREFIX, ) from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog from sqlbuild.compiler.sql_analysis.main._identifier_case import ignores_quoted_case @@ -526,19 +529,12 @@ def _assemble_compiled_model( dialect=profile.sql_analysis_dialect, ) if sql_analysis_enabled: - polyglot_analysis: PolyglotAnalysisResult = ( - sql_analysis.polyglot_analysis - if sql_analysis is not None - else analyze_columns_and_lineage_with_polyglot( - query_sql=analysis_query_sql, - references=model_input.references, - placeholders=placeholders, - column_nullability_by_table=column_nullability_by_table, - column_types_by_table=column_types_by_table, - inference_profile=profile, - allow_compact_analysis=allow_compact_analysis, + if sql_analysis is None: + raise _native.NativeCompilerError( + f"{NATIVE_FAILURE_PREFIX}native model analysis: the session returned no " + f"analysis for model '{model_name}'" ) - ) + polyglot_analysis: PolyglotAnalysisResult = sql_analysis.polyglot_analysis if polyglot_analysis.analysis_succeeded: inferred_columns = polyglot_analysis.columns fast_lineage_columns = polyglot_analysis.lineage_columns @@ -553,12 +549,15 @@ def _assemble_compiled_model( placeholders=placeholders, dialect=profile.sql_analysis_dialect, ) - inferred_columns = infer_columns_with_sql_analysis( - query_sql=analysis_query_sql, - placeholders=placeholders, + inferred_columns = infer_native_query_columns( + queries=( + NativeColumnQuery( + sql=analysis_query_sql, mode="parse", placeholders=placeholders + ), + ), + profile=profile, column_nullability_by_table=column_nullability_by_table, - inference_profile=profile, - ) + )[0].columns elif sql_validation_enabled and model_input.sql_validation_enabled and not syntax_validated: validate_sql_syntax( query_sql=analysis_query_sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py b/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py index 77d79afe5c..b36912be17 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/semantic_shapes.py @@ -9,7 +9,7 @@ from sqlbuild.compiler.analysis_session.main._infer_native_expression_source_shapes import ( infer_native_expression_source_shapes, ) -from sqlbuild.compiler.compile._helpers.analysis.columns import table_function_analysis_name +from sqlbuild.compiler.compile._helpers.analysis.reference_names import table_function_analysis_name from sqlbuild.compiler.compile.constants import NOT_NULL_AUDIT_NAME from sqlbuild.compiler.compile.models import ( CompiledModel, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/source_bindings.py b/src/sqlbuild/compiler/compile/_helpers/assembly/source_bindings.py index df2eb07e2d..5fdc739a92 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/source_bindings.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/source_bindings.py @@ -7,8 +7,11 @@ from typing import Any from sqlbuild.adapter.contract.models import ColumnInfo, ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, +) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns +from sqlbuild.compiler.compile._helpers.analysis.binding_requests import ( get_complete_schema_binding_request, ) from sqlbuild.compiler.compile._helpers.assembly.binding_positions import ( @@ -32,7 +35,6 @@ CompiledProject, CompiledSqlExpansion, CompilerDiagnostic, - PolyglotAnalysisResult, ) from sqlbuild.compiler.compile.types import ( CompiledResourceType, @@ -125,16 +127,19 @@ def get_source_binding_diagnostics( table: dict.fromkeys(shape, InferredNullability.UNKNOWN) for table, shape in inputs.items() } - analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=model.query_sql, - references=model.references, - placeholders=_placeholders(model), + analysis: NativeQueryColumns = infer_native_query_columns( + queries=( + NativeColumnQuery( + sql=model.query_sql, + mode="reanalysis", + placeholders=_placeholders(model), + references=model.references, + ), + ), + profile=profile, column_types_by_table=inputs, column_nullability_by_table=nullability, - inference_profile=profile, - allow_compact_analysis=True, - recover_cte_facts=True, - ) + )[0] if analysis.columns and (not analysis.has_star or all(inputs.values())): shapes[model.name] = published_model_shape( sql=model.query_sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py index c6d447f607..78ea48e8a6 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py @@ -7,6 +7,11 @@ from typing import cast import sqlbuild._native as _native +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, +) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery from sqlbuild.compiler.attachments.constants import ( HEADER_ARGUMENTS_STAGE, HEADER_METADATA_STAGE, @@ -25,7 +30,6 @@ NativeFunctionNamespace, NativeFunctionNamespaceInputs, ) -from sqlbuild.compiler.compile._helpers.analysis.compact import infer_columns_with_sql_analysis from sqlbuild.compiler.compile._helpers.analysis.validation import ( validate_function_sql_syntax, ) @@ -404,9 +408,10 @@ def _validate_table_function_output_contract( return_columns: tuple[FunctionReturnColumn, ...], function_file: DiscoveredSqlFunctionFile, ) -> None: - inferred_columns: tuple[InferredColumn, ...] | None = infer_columns_with_sql_analysis( - query_sql=body_sql - ) + inferred_columns: tuple[InferredColumn, ...] | None = infer_native_query_columns( + queries=(NativeColumnQuery(sql=body_sql, mode="parse"),), + profile=ExpressionInferenceProfile(), + )[0].columns if not inferred_columns: return declared_count: int = len(return_columns) diff --git a/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py b/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py index 709e95c39a..e99be95a28 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py +++ b/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py @@ -8,7 +8,7 @@ from dataclasses import replace from typing import Any, cast -from sqlbuild.compiler.compile._helpers.analysis.columns import ( +from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( _analysis_reference_name, _lineage_resource_type, _qualified_reference_names, diff --git a/src/sqlbuild/compiler/compile/classes/python_model_analysis.py b/src/sqlbuild/compiler/compile/classes/python_model_analysis.py index 311ff4dfde..b3005470bc 100644 --- a/src/sqlbuild/compiler/compile/classes/python_model_analysis.py +++ b/src/sqlbuild/compiler/compile/classes/python_model_analysis.py @@ -4,7 +4,7 @@ from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.compile._helpers.analysis.cache import record_analysis_cache_metrics -from sqlbuild.compiler.compile._helpers.analysis.columns import ( +from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( substitute_placeholder_defaults, table_function_analysis_name, ) diff --git a/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py b/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py index 928ffc73e4..3d0c265b2a 100644 --- a/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py +++ b/src/sqlbuild/compiler/compile/classes/semantic_completion_inputs.py @@ -5,7 +5,9 @@ from dataclasses import replace from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis.compact import get_complete_schema_binding_request +from sqlbuild.compiler.compile._helpers.analysis.binding_requests import ( + get_complete_schema_binding_request, +) from sqlbuild.compiler.compile._helpers.assembly.metadata_validation import ( _audit_errors, _names, diff --git a/src/sqlbuild/compiler/sql_analysis/constants.py b/src/sqlbuild/compiler/sql_analysis/constants.py index 7de538663f..7cc6861c9d 100644 --- a/src/sqlbuild/compiler/sql_analysis/constants.py +++ b/src/sqlbuild/compiler/sql_analysis/constants.py @@ -183,3 +183,5 @@ "sqlserver": "tsql", } ANALYSIS_NORMALIZATION_CHUNK_SIZE: int = 256 +TABLE_FUNCTION_ANALYSIS_PREFIX: str = "__sqlbuild_table_function_" +NATIVE_FAILURE_PREFIX: str = "NativeCompilerError: " diff --git a/tests/goldens/python_oracle/cte_facts_casts_case_coalesce_if_iff_adapter_rules_set_operations_stars_joins_filters.json b/tests/goldens/python_oracle/cte_facts_casts_case_coalesce_if_iff_adapter_rules_set_operations_stars_joins_filters.json new file mode 100644 index 0000000000..b7ac4fbe19 --- /dev/null +++ b/tests/goldens/python_oracle/cte_facts_casts_case_coalesce_if_iff_adapter_rules_set_operations_stars_joins_filters.json @@ -0,0 +1,20002 @@ +[ +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c4aa1da0f1d478755dcf8a3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7dba6a1e770fcc198f69d145" +], +[ +"cte_facts", +"e55706d39e555ea7115c576c" +], +[ +"cte_facts", +"87ef0419b3ea862c9735302e" +], +[ +"cte_facts", +"bdcb307145ced4668afe64e3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6d76f8a48f9b12ee518e60fb" +], +[ +"cte_facts", +"5ffd2069d857db078b0ec5c2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b8e380077825cf182ace2d99" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"d93f5e8f59a3d40112e9332a" +], +[ +"cte_facts", +"17c588f2bbb97a4bc13443f0" +], +[ +"cte_facts", +"fd8b37a35f3e2c74c92576d8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"19b25264866922f98a2dcb19" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f3b58b842ed9380d8ae1ce94" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ad9c73dcb08b7eacb70299e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fbb441f302177546e3833646" +], +[ +"cte_facts", +"2ac4e5c7259c7cbd45a09753" +], +[ +"cte_facts", +"a4b0ab53b7f8c3a6975714f1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"359f7b43ff014fb622d621f9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9573ce4ca7b2b1fba23b8c84" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"95e71c4565200f73840d303d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ed71610322a74156754f5d27" +], +[ +"cte_facts", +"a48132b7780ae8d603d76900" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"ca1a177f7157dec08bb1bc7c" +], +[ +"cte_facts", +"039ac124b2350f419e7ccd87" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"343133878e398b8afc9d4ec2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a1393e4430e2d96e740ed4d3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"864639f359cdbdd39a1f08a8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"653a5d5e3f9a849d912c7f85" +], +[ +"cte_facts", +"02745ecccaca07e75093e1b1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c868975bfd30e3e6785859d" +], +[ +"cte_facts", +"2ad9f23db26467e4e2f389d1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"204bc8bed3e771cc7eebc037" +], +[ +"cte_facts", +"e424f045b964a22cc93b52fe" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d24d917c7ae3992ae654e52e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"681af4ba9ec9ff73ebe5afdd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f44562e2016b7337866b3ef1" +], +[ +"cte_facts", +"c742d17ed46b2233ba193beb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8aa0bfe1294c418cbcea98a7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"75898bdb63df78bcf19c6b21" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b7ed9bf45e0827c61c44143" +], +[ +"cte_facts", +"8199e5ad510ba5d0bd0d20ed" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"7e5cc2be9df60dac53d4f891" +], +[ +"cte_facts", +"07366cdce02e55b0f4364c50" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"7521fec50dbc20c6306c7cc6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"08385877e1a9d2a18c9ca75d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0ba77f9f2c880134f9ae0ce1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"35384c58f5428d4c096a4569" +], +[ +"cte_facts", +"f2176833ed4d1168120afec4" +], +[ +"cte_facts", +"5a85c76baa2f4e4dc772787d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"42aa8a2fc4d0488a5eee3f8d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ba9cfeda084c6e74101948f6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"085967ae5ed3a757c5b0feb8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8bda81857f610884feee3230" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b289d13480a6673a743e82e4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ce3ed0ce92618374381ad418" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e3e721cf187332480c9e5ad2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da39cb61a8156b9d35e40ff8" +], +[ +"cte_facts", +"f5895c1d77c8f590ef4e7477" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"79c08cc8c06967f247cdfd09" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3a48896ab8bf836265be5643" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ec0da57e1e89b3e0b93227fe" +], +[ +"cte_facts", +"075df3a32942712e6d91b09e" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ace5a6c00909b0bec1f2aefc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"e5d6faf13655ed5a2dfdeb02" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a00bc1aea089f5badf154464" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"826bf6453b49a73cdf53aec8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1bfa15e991eb93ee9aeadc37" +], +[ +"cte_facts", +"9930623e1aee198195f1d0fe" +], +[ +"cte_facts", +"58392bbc2de6742937d7a68b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"edd5f8f92b661b4075de3702" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aab135855fd57bd42d0abf49" +], +[ +"cte_facts", +"915f9b197daf381c8ef0b5c4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e8dc441dbb5444fff5ef76fa" +], +[ +"cte_facts", +"9538db3dc0abfe1068ef4dfb" +], +[ +"cte_facts", +"62309c4535558a08999e41d7" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"00a7ec97323806d55c3e51e6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da296187cb7225b157fd6ff6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1b09452e6b382fa38fd04ac8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"03cb6976ba20a18a3ea5f850" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"903b989eb8e513c673a41ee5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dfed7518ced05f2523d2be87" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e1193bd17eb647af362b2c6b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7217b115b977b29e5404ca4" +], +[ +"cte_facts", +"3277f806a210ac9a80e25cb3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b1a1b16aba91eb020793d3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5a74f0d5ac433c353777d637" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9e7eb59bc3b77041fb934759" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a11994b9d5efb063864097c4" +], +[ +"cte_facts", +"18417401649ba8eb931f5f1f" +], +[ +"cte_facts", +"a20c70ff116575d08a066320" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f3256d9ef6946683c94d4150" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"24797cad26dd72d1f1a0a886" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"5a453cd7b93ed7b68e6f8de8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a562adac793967e5ac560f2f" +], +[ +"cte_facts", +"a928caabb1ae4d21f12bba19" +], +[ +"cte_facts", +"9846802632ecc9c9e6131e20" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"123959fc913a0db12bcc1de7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b2fd164aefb6da8aa8962bd4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2d73f308ba410450fb15e37f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"847b413032c1e5993f51c539" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6288ed72565ac4e60d67e5ac" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5d81e113a2e67899c88cbc1c" +], +[ +"cte_facts", +"8a25919eaec2b38a1643761f" +], +[ +"cte_facts", +"acb0c618555111c112ae71c2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6e3464bf07093435340eefa6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"e112dab990efa8668b76b7ac" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"06e4c5dee9a367aad4191ceb" +], +[ +"cte_facts", +"172ae9b0adad29f893e6ee1e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b28020a95692a449f55a5fdb" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0f2e0f6a350ede31cde1d277" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1766b78edb98f037adb93443" +], +[ +"cte_facts", +"45600a76b057f3bc8d7aa623" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0fe37f13ac7d5d3af376d7ec" +], +[ +"cte_facts", +"90dad19621993cfadf9abb81" +], +[ +"cte_facts", +"024ff35a53aaaee5cb977edd" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"945fdae8cd8736881f672638" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"68706a47d024899f769eb2db" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b387f20e3aa10fe75788fb57" +], +[ +"cte_facts", +"c7240994805b0343e1f7b508" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f12cafb0286960a1cf50c8e0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5ee078ffad2c2dd7de9d273e" +], +[ +"cte_facts", +"e3378a1959667c216051398a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38202ce0208447eb438ff5e9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1d9a728cd6935010f4b9eb67" +], +[ +"cte_facts", +"f90294ebb9638ba3b9ad7280" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f4dbb308e7c8e371d6be6109" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"abf6ef72e65b008efcc58f84" +], +[ +"cte_facts", +"c007d695718a581c99533580" +], +[ +"cte_facts", +"8213eaf4cdd52bbed31c0402" +], +[ +"cte_facts", +"79126c548cd04db1361ce77c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6b1968a71df44affd6ca82ec" +], +[ +"cte_facts", +"4a4dd3e640fbf97d1b20b4e7" +], +[ +"cte_facts", +"a1393e4430e2d96e740ed4d3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f41357edc80bde20af9b02d1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"13018cb6e748ed66382b9663" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0fcd016def2c6a87bd549112" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"6d5b9939ae74a230637f0e55" +], +[ +"cte_facts", +"6d76d2d25d077d84fd780b34" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"681cf8357fb16b851e3096eb" +], +[ +"cte_facts", +"60b233038b04045136a907b6" +], +[ +"cte_facts", +"1fd9c3fbfd67a1908c9a796b" +], +[ +"cte_facts", +"606c7a9480f49d5612d66d2a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1f8dbc9ba9b0b39e455f8aea" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b751dbe75bd57eb0270e5f25" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"2fddbf292233c79bcaaba2c2" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cf56d985716a981f1117d243" +], +[ +"cte_facts", +"02a6a797e954c6207879bfb5" +], +[ +"cte_facts", +"4821a73aa15fd91072beb470" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9efc74715706347643980677" +], +[ +"cte_facts", +"24d17721c79882b0a6454187" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a00bc1aea089f5badf154464" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6df197f15020a677b51f12d5" +], +[ +"cte_facts", +"3cefaac56bac25fdb8a0c2d7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d8d334479feee0848404d6d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"74c48b6f726adfcd4d31a1d1" +], +[ +"cte_facts", +"c890db40a2f2ab2c538ef503" +], +[ +"cte_facts", +"09dc74b9ec8a19ca0e931a6f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b2abfc225ad5b092fe9ac4a6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"47fc474e7873313d76b77a4a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a3e2165aa660f4d87a8fdf83" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0ff36f2b49db0e4d671f2617" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f1d1b41c96997d7e6de43873" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"281d8ccb4f1abf88fa77c482" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"308949c4abeae0319d98caaf" +], +[ +"cte_facts", +"5c2dc0b2057d0c2922975568" +], +[ +"cte_facts", +"8f8370abd4d163c28199f1b7" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"e472c1a16e9196ae9c6c8fd7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bcb2c930000e14237701e41c" +], +[ +"cte_facts", +"6c32e27c13d903f4d74d8897" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e58e07048c41f9428f391a12" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"39ed39fa998f81756bf1428f" +], +[ +"cte_facts", +"6210461521a0458ea6179424" +], +[ +"cte_facts", +"b53da886256c402811142838" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"03c799c49085d71b734a8387" +], +[ +"cte_facts", +"dee5964f5d2de02ff70d818b" +], +[ +"cte_facts", +"006f45db244fdae25f809555" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"27db24149f2eb0ea4bec43bb" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"31ce05446f06237043d47233" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2a3244f3e68c2fa3b469e0e6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ef6098dfd72b64b656c91b5f" +], +[ +"cte_facts", +"cc5b8b5d6a66f8fafdd62656" +], +[ +"cte_facts", +"10e8544574f2f945ffae2040" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cf121293d0da0cd5c19bc028" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"6820c680b3e1ead11bf4a810" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0e5a93beb28a7907f982f2f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"329741eafdfb707c0754b2a8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"784ea83c107483495cf058ac" +], +[ +"cte_facts", +"47a1eac84943176c4f7a9804" +], +[ +"cte_facts", +"e316b93608cdb7ad0f0b8b3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"483ff2f29b13c836c87aaa58" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8f8370abd4d163c28199f1b7" +], +[ +"cte_facts", +"8e7c297f23d00786ef682bb6" +], +[ +"cte_facts", +"10eb7c78ba7a51052f84322b" +], +[ +"cte_facts", +"fe03ab15d1c955db36241607" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"116f83c4e2caf8ff0449e46c" +], +[ +"cte_facts", +"66b1e27d5c7221b41db2a29d" +], +[ +"cte_facts", +"109c5b2531d5283555d42bf3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"988cfb8925411f841ae56bc8" +], +[ +"cte_facts", +"3cba35c370aaf50c7489c5e9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7a00046e5722e993b5b0e35" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c9aa0341836c73978257bd2c" +], +[ +"cte_facts", +"b8c4da60c1f4ccd89c98d3bc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f331e74518af118048b1c22f" +], +[ +"cte_facts", +"3c733fbd97f11c1ae39ee072" +], +[ +"cte_facts", +"5cf31fa4748a07198b799516" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e67d658f2f46e2727c497700" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e0b5081e91e76f874870f56" +], +[ +"cte_facts", +"9c3cd032cba764839a7c50f6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2211ce1dc3b63b8634d7e29b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5719f687b441d45b1b1f6262" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"791f0c0098a88896baa49451" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b5630daadcfeb802c880b22b" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"ba45f9e7a0225156bcb5ebfa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d2489c5742c84311be0ca63a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0066561a40fd49f80b37f727" +], +[ +"cte_facts", +"203f7a3902dd81670be0cbfb" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"380265d92939e2ba3160c23c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f50d670cc1e96b61fad904cb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"71113f45c0f00ae666dfb16f" +], +[ +"cte_facts", +"22b7dad8df2a84eae1fa80ce" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"abcfe8ec9c9c62480ebc7f99" +], +[ +"cte_facts", +"d4eaa039373b942c57e7e468" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6c21dd5f85e82727a1bce8c3" +], +[ +"cte_facts", +"7cc40af0e041262d96463592" +], +[ +"cte_facts", +"71eea0018d52ec2098e37e19" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d42403f29355378a9219f95a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c655926ac346061e33cd5aa8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cdb86a0ad527a6e25dab3363" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"87627cc58e40e20711a025e2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2885b666e34bf5f69d5ba345" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f100433a6ddd67cc3adb0d90" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3be2ce131c1b738099e48896" +], +[ +"cte_facts", +"f530e38a1e389ba3589607d5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"87650811bfa009b0feaf9bf4" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"93d80d968a5aae65cddc194d" +], +[ +"cte_facts", +"5f479551e0eec78c41e71ba4" +], +[ +"cte_facts", +"eb95bccc50b3d769903746d6" +], +[ +"cte_facts", +"72f8e2dce9458a0213c2d2e2" +], +[ +"cte_facts", +"78ee6edb6a140a35daf6171b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7591e5efd7f226ea6648a828" +], +[ +"cte_facts", +"0bbfc524e9661c142ed7451a" +], +[ +"cte_facts", +"3309b324a7affadc48493aa6" +], +[ +"cte_facts", +"5ab72e089388d9dacf062f24" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a1861cd3e0566037f48751c" +], +[ +"cte_facts", +"95c582ea331b1c5498bfe485" +], +[ +"cte_facts", +"8d072c14a9d47791236ccce6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eed6fb3c58891d0068bac226" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"717f9807521044ccd494c02b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f9ddc19d61db0a1cf577c901" +], +[ +"cte_facts", +"a707c5b5165a27fc99861e5e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8f8370abd4d163c28199f1b7" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e6f4cafeb0816cadeda8442" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"22fa8b6e8197f352e18f42f5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"256299c4516f7c76fffd081f" +], +[ +"cte_facts", +"b72e68f738369de9b4c48790" +], +[ +"cte_facts", +"9d93133a60e49fee8fce8407" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7f7f8f9b78d8b925e445416b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8994f278d44f6025a9fe0dbd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ceb1f8c590d035be75a0a11" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"60c0c75661d495024c69323a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"54a8494778eb2bcfa7a02231" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f41357edc80bde20af9b02d1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"54718ce1f1c11fdde82afbe8" +], +[ +"cte_facts", +"4438d42825736fd903a3f0e4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3beb8118486379f13a539b34" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"77b6c73fcf5946b9f7ac14aa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d7a4345e265c6f5a312e4540" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a34aca63995de9eb9a80b25e" +], +[ +"cte_facts", +"8d3c7a6e4d4db73443ad3afc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"86b6d38cc3cce4981c3613c3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fab4e282daf75896518c98e4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4267c6ef3c4b0e04fc1f534d" +], +[ +"cte_facts", +"f311fef1ccbce6eb8cf9429a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c81147291ef378f27d56bdfc" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"c372dcd661f06065515e73e9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"44e93b1bd1331e5681040334" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7a3eb07506b9f2b59a597a1f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"8a25919eaec2b38a1643761f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"16275f1ddcbbaf8f2df584e7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"093e96245cc5209d4cbaeeba" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0f5f2c7f4bfe8e219c4cae64" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c3f795ce70c03ceb6b9daff8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5afa88ea2136b6a2e0d3a38b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d0158c58e7eeeb01f2c7eeb8" +], +[ +"cte_facts", +"15d0e4cf583be4aba460322c" +], +[ +"cte_facts", +"9de02647b93dc61457319f68" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d30777b977850bbb40133d05" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"44f1dfbe9a8900c1a9feced9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6f4902b97556de2379c5efaa" +], +[ +"cte_facts", +"533f30891827411589ceca56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"43be6bc915a43316ede185f3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d423a9ee75a313ba0ac74340" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8c7ae05db549df52437873a9" +], +[ +"cte_facts", +"d8c4e1a3927b8f7d6ac5508e" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"10380b7e75ea91f69fa626f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4faacd50fddae2c8bf989322" +], +[ +"cte_facts", +"ea4e83b0cc26afd79f3d8458" +], +[ +"cte_facts", +"045618110a08e3aba8535fca" +], +[ +"cte_facts", +"a85fcaa6808c48fc9f7bec80" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a9c7c5354062238c0d6f2471" +], +[ +"cte_facts", +"2e5876c579421961f3ac5e88" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5d319a9b513c464da65780f3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6469cfea0333baa85290dfbd" +], +[ +"cte_facts", +"ccb3ca9faa2a2282e1a880c5" +], +[ +"cte_facts", +"7c8a9dbd956abe11190a9e35" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f4faaf760631624f1129378c" +], +[ +"cte_facts", +"a28af4ef3973749da47c6efb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7fe291699d06e6f801b0b3d2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"54b2cc62496239f28826bb7f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"deeceb5570e18756f333224c" +], +[ +"cte_facts", +"b622ed75a6cbaf518f37ea6b" +], +[ +"cte_facts", +"62d6915e88a664468aa33a9b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"c481f08eade4324aed8dfc29" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0c6ffc23cf231c73af4f8dee" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"971cb38e6d436a6cc035a32b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dd44d632b2b04f3899f72c21" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"10eb7c78ba7a51052f84322b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a87c7943afd394f6ae25403a" +], +[ +"cte_facts", +"821689a7a3e12c0d14dcfdcc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3658079b0bda58d02c8c05a8" +], +[ +"cte_facts", +"ea3b39581bfe93505adabc66" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0d74a69a8a5156281e3f2c06" +], +[ +"cte_facts", +"00b0ed38c8fd1aa0bfed91a7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cd063b7695dcb3d2a337d35f" +], +[ +"cte_facts", +"2266f190ec921a04c95473a6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c1ab292d0c08374fe35dd66b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c18610f62881611ac3a56785" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f1b42ec21893a4c35ec4ddc1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2f64cd14dea2a987f8ef3df7" +], +[ +"cte_facts", +"b5c98e4f0861a9f86f510633" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8376b2f6625ec70a4b5679a6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4210f03a445d2854e9cb2884" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"072c5bf2ceeb89ce0dd6f905" +], +[ +"cte_facts", +"85cf1023b521c603df7c680c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"10abfab7ff22045b320b29d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"52bfcc544e1562d6cf4f2aee" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e519f60e4183232aebc79aca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6322aaddede94778335eb00a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3e3b087f4c2846e834a8e822" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b404172620c8f1f2f5cf564" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d418b3d13418a7326edd4dcb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cef999db49ecf911ed2963d3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"56b5e7e1523b53ec07eab721" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0a23d441d2369f2cab3b5d02" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3518df502ad92141ba65ab80" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45600a76b057f3bc8d7aa623" +], +[ +"cte_facts", +"cad111dda2e97bb573884b77" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"3a9bf5cc8ff8bb0b49f2413b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9829ed0c99f7bc45acafe6f8" +], +[ +"cte_facts", +"20616b7443884ed31d002f1c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"72cfad6995ba8a98867eb76c" +], +[ +"cte_facts", +"e349e079a068748626a741ff" +], +[ +"cte_facts", +"4c8999523b680e6658a4927a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7d0a3c31eeabdef193c78b9b" +], +[ +"cte_facts", +"6c243a3c44675de330f98e0d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d2a983eda00be6963424d04c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ffcd5286466af711ccf0c5a9" +], +[ +"cte_facts", +"c65ff553b876af4ee7bf360f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"403a84c001658bcaf682f913" +], +[ +"cte_facts", +"ce64de05cea0dbeffe392a78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"682ea922f2e010d016b3e82b" +], +[ +"cte_facts", +"e22acfe2eb5978367a4d13dc" +], +[ +"cte_facts", +"50550ed36eab4ead7df032d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e8799445c9d370ac5ee84acb" +], +[ +"cte_facts", +"eb27411a15000e74077716cd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7f417e9963bafec74b192e8" +], +[ +"cte_facts", +"a4cf7fa9ab41dd424494a453" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d3c50785aaa5bb86fa58309b" +], +[ +"cte_facts", +"00fd89b5fc22e79c877eb65d" +], +[ +"cte_facts", +"c91b6caf02be38ceca64da20" +], +[ +"cte_facts", +"9a1604d2aba32886ff141d17" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"70a83fa8faa7241de600a62e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"717f9807521044ccd494c02b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cddfe9511ccf6d8b9b49ace3" +], +[ +"cte_facts", +"e11249b1be1dfcf4994830e3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6f967aed45f75589baec1064" +], +[ +"cte_facts", +"699cddb166e4d10b46c751d3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a493dfdfa46884ab30b7c2d5" +], +[ +"cte_facts", +"648fce592e474a51a8b8fe6e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ef2fbedb17c498b2fb208591" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ff88bf913e03c73c4014edf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"16161bc32b9ba2887a46339e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a28af4ef3973749da47c6efb" +], +[ +"cte_facts", +"a7519eff523c32bdb14df8b7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cd8469ce3ccd15a1c382bc75" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"17c155f5051dec610cf1d7c3" +], +[ +"cte_facts", +"26b57013b89a8760ea1c0b02" +], +[ +"cte_facts", +"7dadb5e76ee2a386f8989acd" +], +[ +"cte_facts", +"da85f23769ecb0b659bc027b" +], +[ +"cte_facts", +"50a4033641545f5af091f0c4" +], +[ +"cte_facts", +"6c5ad24cfb63d828de3fd55f" +], +[ +"cte_facts", +"b1b1a85f62cdce1ce48bc72e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d7d418253e6b9047f009f94b" +], +[ +"cte_facts", +"879e8fa5ec50584af1c5ef12" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"600bf5e5cf97ffeac7a142ef" +], +[ +"cte_facts", +"acc890e354bfbe8cb3c12266" +], +[ +"cte_facts", +"a15ddaa580d32f77d6166fca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7856143b5b9ca78818234b3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"15098845e756d79339f10de4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"18de50fa234011accf6c888e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"1fbbda2053911f1b6732e2c2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"30e28ae34fe2bdfbf7984569" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fc73d2937f51b39800f08d79" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a3e2f1c61882c647b7ad1606" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fafde00af0b403c065be9bd8" +], +[ +"cte_facts", +"4017554cbedd50d1d663cb1a" +], +[ +"cte_facts", +"724b4f87bc24e04a242abc4e" +], +[ +"cte_facts", +"0cadbf4ce39ca81eacb0a281" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a902825ec0f49b9a7582ebf7" +], +[ +"cte_facts", +"e9df0d596f8baaa218efa6bd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3df0f6801a6bc31ff67743c6" +], +[ +"cte_facts", +"93bb657ded7152f4c013951c" +], +[ +"cte_facts", +"eb0efbb3b8901a1d577bed95" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"5e63bd95425ce1c8fc255b1b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"46b4b6d25d42544f2e7bf6ae" +], +[ +"cte_facts", +"6a2520f074d54da8f620c9bf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"32815c8454b772eacab325c4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"01424d7107cbfd6d4092bcae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"18f17ed14fb6f31dab9678de" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f124eb146f0e87f82092b295" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c15b9f9104e33ea6ddd0d1bb" +], +[ +"cte_facts", +"f32f1dc3c953c65fb60a8409" +], +[ +"cte_facts", +"eeeed8fc1171dc7f5ab3ffa5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7a6fe2670b633b7236bccaa8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9a71ebd527cc7f6809ec410b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c98b2f2b24e00badc6429e6c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c62cd1aac4add9211f48320c" +], +[ +"cte_facts", +"6437ba15995efb77ff972715" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"15ff7d6fd44a58aa62053116" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"508cc53138f894e5627f48e3" +], +[ +"cte_facts", +"6d4b88a0d215c6a1be9d389d" +], +[ +"cte_facts", +"48f28a55437fbc26cc953a36" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a00bc1aea089f5badf154464" +], +[ +"cte_facts", +"a864cdac67cdc2df9420b582" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"176236cffb1d1348a8a95a3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45667988a22246c62f1933ad" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7f2f3fd963b1442bd48cb26a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8b50460ef85cc2e406ce9237" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7354aafdac4123877aeef378" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aa037e35d249f604369a9440" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9df6f7f4c8d934c60e16ad14" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3dc57dea717e159008b46ebb" +], +[ +"cte_facts", +"ddc453c4206a43050d3b6049" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"42aa8a2fc4d0488a5eee3f8d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"689c627180ab2d3095c16e16" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"af4ef5c4775a8788ed7cd06c" +], +[ +"cte_facts", +"86caa0f97baef00d6a65c92f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4829c1b6749ee3f0daf22aaf" +], +[ +"cte_facts", +"39e1e270de512b0a6885ec36" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d7b3fb8077eaf1c2fb780267" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3cc8ed52f4c2c5c9d7b57220" +], +[ +"cte_facts", +"03ebaa5c774b130e8767f57d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"34ca064c10296ffccea0a5f5" +], +[ +"cte_facts", +"e00aa2da2c3fe8a5d52183bf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bdf3304dd619954c5b054408" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3e8a0f7c60497164c54a54bb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8b371600511fc7556a9cb430" +], +[ +"cte_facts", +"7ed64be8384fac306cbfd6cb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"866eaf511c16d9d0e6a5895d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55b1bbfe9e280b444aace2df" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2745e60c90656bcae17df793" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fee32a84edda43487e9851aa" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"85e9113cb994dddc1fa3a0b4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3dae2a2648fc31a8c0f68813" +], +[ +"cte_facts", +"c00c29b0f7cdc26397c61c73" +], +[ +"cte_facts", +"56ba16ccbc1a35ee2d8194f6" +], +[ +"cte_facts", +"d72b1c57ff00d40c2e18baaa" +], +[ +"cte_facts", +"c1a6786c2a7333279329f9ef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ec1cf562821f033e0c882b80" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38a3fce16527519e5a47d4dc" +], +[ +"cte_facts", +"90387527db320fd4530d0d65" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b6fc91d919272da017c90906" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"89f2d9e16f0b43e53c05fd8d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"889b6c62ca816f4a80ad650c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e90addde127fb134ee41b2a8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"746963960f2bd5efa4a6c29f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cd543ab9b7396d384d83218a" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"222d7b52bbdebca10d580a37" +], +[ +"cte_facts", +"474dab40da8cffbe8ebdbd23" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0eaa00d71fed762eeadb364a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"04bd69854331607540553a09" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"870bae4bb82d707918bfac02" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"74d1eb1fdc6459093b34c2f8" +], +[ +"cte_facts", +"340d1242609a9cc38b705454" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"146bf7bc8cf050902c1c0739" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2f7b10931a47cce1fad6aca1" +], +[ +"cte_facts", +"3013bf7ed9ce610e158c3e9e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f2d2443cac2d1cb73d075191" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a8fa44034b5eb021bf07ee9e" +], +[ +"cte_facts", +"ae6cc3a921aa9e65f4eeea1d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e4c0ce0c6a71926b53e3185" +], +[ +"cte_facts", +"399fbc7c26192a531216e512" +], +[ +"cte_facts", +"f0ef2b9cb40550c9e8b6f5bf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f3810b404f0c2d48fee96272" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"331e46e44f675c4402d03ab9" +], +[ +"cte_facts", +"4a1148a10b4ae1a1a2cb4ac4" +], +[ +"cte_facts", +"20eb4d1ed598a393febf0aeb" +], +[ +"cte_facts", +"6a7f99ab3b34b975098ed78d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d8d7d9b0b50847f0e16c04a5" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"31ce05446f06237043d47233" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7f25145c4f3ba9be42315bd0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"90387527db320fd4530d0d65" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"4829c1b6749ee3f0daf22aaf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a15a6c8b36eccf68bd084a71" +], +[ +"cte_facts", +"aa7d182ec04bf5ab611a1e24" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"23902d77fb9b4aaaa3ee1134" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ecb31e7eb76f1ab840d61a72" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1aadee58327c359b7f20b9bd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ed719fee5997eba94c6c4e82" +], +[ +"cte_facts", +"176236cffb1d1348a8a95a3a" +], +[ +"cte_facts", +"be5c26ec5c61e562e525af2c" +], +[ +"cte_facts", +"03d28eb7ba964b869666b1b8" +], +[ +"cte_facts", +"1a10de1f046cd2e9040ba228" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6c66d510b5cdd3be11708f29" +], +[ +"cte_facts", +"6c7bfac4d071e2e577cd2047" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ba8ae129584568609efa8ec3" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2fcd2b715aee970b09d1939b" +], +[ +"cte_facts", +"fa12d3dbbd5af3efdd7f0421" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ab537a44bfdd87c1584026da" +], +[ +"cte_facts", +"0474da9ad560751c44fabff7" +], +[ +"cte_facts", +"6322aaddede94778335eb00a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f07df18b5f3ced96efc291e0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d93f5e8f59a3d40112e9332a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c6b9e7bd79af73a1f216107c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da96cac1ebdeced6c3246ae8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2990a9e80ac314ca21fae9c8" +], +[ +"cte_facts", +"5486820130e7d072faa3cba9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9a99cc03fbf906873ee7e937" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cacdbaa5df3670bd4f7a9b94" +], +[ +"cte_facts", +"8d828bb774344e50a7735dba" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"0786540577b0ace95d71ad84" +], +[ +"cte_facts", +"32815c8454b772eacab325c4" +], +[ +"cte_facts", +"3ac5ee10f91811f01944d9c7" +], +[ +"cte_facts", +"1e0b5081e91e76f874870f56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"18b667356bdf7d0a5fc06f4b" +], +[ +"cte_facts", +"27197b5a18a8c3e48f949dd5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eeb45e2a9b64af1b8ad097d9" +], +[ +"cte_facts", +"7ee701be385e1ae26d7a2208" +], +[ +"cte_facts", +"6e5df2a4b157c0255f44d28e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"806b51bc7079e88baf49fe67" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"52beb074b74156ba426d1a77" +], +[ +"cte_facts", +"8e5638a90799fb743c2841ed" +], +[ +"cte_facts", +"2d26c04a383ddab2aca4e3d7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"18686fdcdb2615b70dc0360a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f26b279b0b41306ae3c96aa0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"80414a3979b2e356937bb2ed" +], +[ +"cte_facts", +"b83b74a4dc3fff3aa91284e1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"77ec9ddd6051a57c72242867" +], +[ +"cte_facts", +"c0d665860fe9c17f70942d5a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c38762f662ff0b7de6e10d92" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"81b6f3fe4406c201014229bb" +], +[ +"cte_facts", +"3211527037b1f7e8b09ce127" +], +[ +"cte_facts", +"be421c74025339379c86ac54" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e0baa91f7f0662b887678387" +], +[ +"cte_facts", +"ea96e238b5198477bd30504e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"948432641f53792583d62aec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1a0fdfb8298ca9abc215790b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bdbd822563b2abbf38be705d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b489acd4f2bfddb195e445c" +], +[ +"cte_facts", +"a764807d918dfed855d0d54b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c0e614b9236f852a9a5a4d88" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38cd134067ce8cec9bcb1eb7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e9a10d9ecc404895bc26b290" +], +[ +"cte_facts", +"1a0c9680b7d085f48d59450e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1d5dac17fd611343add862c0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"212945ada13b0575456393b2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8f496459bd9aeb74fde77092" +], +[ +"cte_facts", +"3526fc0d243926a1b8d1bfd5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2de3863e3e3b27acd68bcd5e" +], +[ +"cte_facts", +"bb28a5d87b552ea2ff02562e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"0ee1c5438cb1674266334a30" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0365ce77fd80a82b3837efd9" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"18aa68b661707540d013a1aa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"632d76e38f512795278acc8b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4829c1b6749ee3f0daf22aaf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9275c572ad156e1371f0169d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5db4de5ded005854ce189217" +], +[ +"cte_facts", +"2de3863e3e3b27acd68bcd5e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"91f33e1f9226951a42cac62b" +], +[ +"cte_facts", +"e0f8c4648393de6d52e21670" +], +[ +"cte_facts", +"1de27371a7b364734e17aa43" +], +[ +"cte_facts", +"a359414d50c42d0f30c62771" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b54b2784662a41c6abbb7858" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"31ce05446f06237043d47233" +], +[ +"cte_facts", +"10e8544574f2f945ffae2040" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9fc6df17d8108b864aa710b0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1597e324edd3cec6f0027ddf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7724aae4d3885eb719cfee48" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6c8dfce830c9c56286adec9f" +], +[ +"cte_facts", +"4ba0ec0516eeaba347ccd059" +], +[ +"cte_facts", +"1304983664d2978cc4f297a4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9b98bcee5e947ab1439259c4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8213eaf4cdd52bbed31c0402" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ff893ebe8b9df3be720d98bc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"83f5cacdeaf0c39078a79c4d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fd89f3bc8252382c92432bf1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"866197cd45179e4e1a259563" +], +[ +"cte_facts", +"f472ed6755ee4e92ac2f5aaa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"68901ad45a6e183434b39562" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"706d333168ed41aa142cb207" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d5c66882e640673c5b89219f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6811049aa7fc3ccb8d8f7468" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6f239d1144bd40c7dd17638f" +], +[ +"cte_facts", +"4a6c2ffa2bfcdf5811d032ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3732b652976d866f62ec1395" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b95f6ef841799064b011876" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a60af443087f4754cfe8b096" +], +[ +"cte_facts", +"ad844c13aea623cb25525766" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8e5962667a116d08801e95a3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9b5d6c8e97490a048652589" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2d9bf12e8bdbe1ec265d90fb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6edce263e0068e32623d3f8c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8c89a24415e398be8b458e02" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38a3fce16527519e5a47d4dc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"23fac08cfeb334ee50515ae5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ee62b965f7f6d45a85b765cd" +], +[ +"cte_facts", +"e239a1686d1f1d9dded75223" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0113b7ad75eed5d36f3e19d5" +], +[ +"cte_facts", +"1c60aaee0389232e5f392c48" +], +[ +"cte_facts", +"4ff9584900e296755295a004" +], +[ +"cte_facts", +"abea53f46b01bfc5c605892a" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"81c3da14dd15a3343fa89eed" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3ba4bdbe76d52b41018f10ec" +], +[ +"cte_facts", +"0ff36f2b49db0e4d671f2617" +], +[ +"cte_facts", +"d80211449eb69949deb46cee" +], +[ +"cte_facts", +"02a5edd85d8890831bbfd64a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c922d7e47e445f529ba3a894" +], +[ +"cte_facts", +"bb55a93c577ebd25e50cf25f" +], +[ +"cte_facts", +"938fb882c31bcfddce96019b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"084bb9024215fc5151ac04fd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"216a02ac2a8b7aec5b84ca8f" +], +[ +"cte_facts", +"e7adf7cb46b2a63cb95ffcd4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9fd5eec71cce4b3955e43c2a" +], +[ +"cte_facts", +"35d41990b57768acc2e64855" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5ace86ed3ce43650d084ab56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"4267c6ef3c4b0e04fc1f534d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"98a21fc76f62ca1100f585ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"93c7bc68db4521dd0b4d935d" +], +[ +"cte_facts", +"c545264f84e0b84c419023c9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e68771101b9d8278e74de199" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f0200c31632a6ed78fa2ddd8" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b5b1fa21f92d46934e93cbb7" +], +[ +"cte_facts", +"9b7ebf36f5738ce9d14b448f" +], +[ +"cte_facts", +"ea7d43dc213da833dc086914" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f0e3ef84de102fb2182ea596" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ed6e7ae60db102a5a8f1e1d7" +], +[ +"cte_facts", +"0e878e41481a463a59693be5" +], +[ +"cte_facts", +"df66bdbfb7145ea9aad66dfe" +], +[ +"cte_facts", +"e77fab1588be25e357c0036e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b517878fdf98bdb3a3085de1" +], +[ +"cte_facts", +"0c977422459721fd1319673c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"bb287c4cda57bb19efdf94f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"7b60be8f794170b6312f29b5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2eff63a7a320516bdf597635" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ffe1dcb3daa1ea5dd9c407f1" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a132f30269b1e41a2c2cfbca" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6147ec54d88cdc185de9b7a0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"76f8a289edaafe08a4e235d4" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1cc31e14451491fc4bf9a746" +], +[ +"cte_facts", +"d8394f37537921d46e9af114" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f82e4c28299beb3fd592ec8c" +], +[ +"cte_facts", +"9b10a032e998f9b508453e2e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"77ad261f2aef5eff170bd758" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ebcda82bfc70b8c3cb7d0475" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4a7fc820c600415923387067" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a5ad95629ef11d8210d6df50" +], +[ +"cte_facts", +"d0136621c65b44dc92cd9efd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2b8f2388d8a80fe648053caf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eceda2463eb3f0a0331d6627" +], +[ +"cte_facts", +"7f8818c0fcf334491f14c81e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"57bc33b3034a86ba92ce61bd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e94c9b3702d0e8ce57f4e812" +], +[ +"cte_facts", +"7fd4cfa846005c876daa106d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f2b8a299663371a16ba027e2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"76fbde16e5018a3338af1520" +], +[ +"cte_facts", +"1bf89ce635efd55eecca19f3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d5f172d538ae498351a18a7a" +], +[ +"cte_facts", +"fffbc2d90b6e24bc27a3cc02" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e616619423cd5f6eb3bf3f7c" +], +[ +"cte_facts", +"d2d6ac4f84f18280c5d4eb5d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"154c6e31a55e2eae556491ea" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"49723f40e6844829d36201ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"588882179f955f02bdd6fedf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1fa2232360e261c0687d0a9f" +], +[ +"cte_facts", +"3fbff07651ac258bb5f10bfe" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"217b0848a62510855d234790" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"001bf04fdeef66eb649c6e68" +], +[ +"cte_facts", +"6c32e27c13d903f4d74d8897" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f4faaf760631624f1129378c" +], +[ +"cte_facts", +"2de3863e3e3b27acd68bcd5e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"398f4fcd11ee6485967e9ed4" +], +[ +"cte_facts", +"b13d6eb2ab09475180774632" +], +[ +"cte_facts", +"7a8b69fb197812dc73337580" +], +[ +"cte_facts", +"b47437e9162a4259914cffaf" +], +[ +"cte_facts", +"f14698199fc21fc01014b1ec" +], +[ +"cte_facts", +"ce2d8d18a31c69eef70bc0fc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c3dcbee73d9514842426619c" +], +[ +"cte_facts", +"32da4cac10364c5d0461ecca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c4608444066c277d37e53df1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f760c556f57cc408c0a8525d" +], +[ +"cte_facts", +"ff94ee1b456022577fb27f97" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"641cb2035ef82dc30d26b814" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fc8c8b97e61a58a1aadd530c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5f9045a269fea413a2c93a7f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ebf46095225224a1bc3e1118" +], +[ +"cte_facts", +"dcfd0ec49419a739e22a2681" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"13a41ef62daa4dec695d25de" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e3d3aa495d85546fd146b823" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2ef709c135d4c85542b034f2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"274ae94d765726de2457ba3d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c568ba6835c4328c9250871e" +], +[ +"cte_facts", +"9a28b41895aafd1d182f896c" +], +[ +"cte_facts", +"2a1b3b25a2755b6450b21327" +], +[ +"cte_facts", +"63da43ff8f839940bee14689" +], +[ +"cte_facts", +"7b335f85f1a65e16456ad1c1" +], +[ +"cte_facts", +"7f8818c0fcf334491f14c81e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c0c9a4341d1bd05389c8202d" +], +[ +"cte_facts", +"ed7a57df04174634583ad8e0" +], +[ +"cte_facts", +"39c207390ec4471a96042576" +], +[ +"cte_facts", +"a96c6210131bf7af2941185c" +], +[ +"cte_facts", +"d4c14ed86311ebc54b9011a9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b1ed9afebdb8d1177b73ebd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0801ec3fce3d1a9ec19e5e45" +], +[ +"cte_facts", +"c776bbb8c981fcc825367088" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"58ddaad738c5c6a4c191e0d3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d5e45e06c322f90ee64b9ae2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8213eaf4cdd52bbed31c0402" +], +[ +"cte_facts", +"76fbde16e5018a3338af1520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"424da9db08cfafb2ed58b48c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bf68c26218e4157f705ac5d6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a6cbd84661cdd47b0319aa47" +], +[ +"cte_facts", +"6c8c882a31428b4daa8e1416" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"debb55071d52e6821ff94dbe" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0304237baca7bf7d3c9b9160" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"562f8cb13dc7c0a591c241df" +], +[ +"cte_facts", +"5d8f7967acfd60307ded961c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dd6f0d2cba110b0f4fc07030" +], +[ +"cte_facts", +"151bb12e03386bade0488e59" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d671dd1568bb33cacba18fe3" +], +[ +"cte_facts", +"0a0319cce89ec0ea67e430d5" +], +[ +"cte_facts", +"07366cdce02e55b0f4364c50" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f74757ac819f89ce2a6edaf5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"110ec58e72dc9f157b48ec24" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6345ed5392a8ffcd509b9885" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"06ef714b8645fe110ef354e5" +], +[ +"cte_facts", +"3b380fdf718163633c80ab04" +], +[ +"cte_facts", +"b7a00046e5722e993b5b0e35" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"03f7e8f3f607b1729449e074" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e0b5081e91e76f874870f56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38f4b4ac40110dd5d6c43ac3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"43affe2cabc459f8f2d5fe8c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bc0564bea4769ff15698b1e9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"846f6db9b87dda404b0c1d38" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7e3e7cde138fff723be84030" +], +[ +"cte_facts", +"adeb378c7dadf8f8e7c78de8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"78ccb60eb824aca93eea5952" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ed14a02df491908326c36a0e" +], +[ +"cte_facts", +"80eddcee4f7d6d50d429f095" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1b7be1486c7c5f5519df1ad9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"376039d137b0dfc3d958138b" +], +[ +"cte_facts", +"728cf1cae2e80446a669cf4b" +], +[ +"cte_facts", +"a9c7c5354062238c0d6f2471" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3e1e55395e14d623d40e6c06" +], +[ +"cte_facts", +"78d19eb5074452cf596ae63a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a76b45855ec0e590e431bd25" +], +[ +"cte_facts", +"67f73487af6cd1ee11fbbff3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9b97e6bd2b57b4b3f232d675" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"850302e48dad146a3ae20ce9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"317e800ef36eb8b5b4dce5bb" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a5c5a86e776178782820b709" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9c3e1cd177ced343021acdab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a510edd83e6b1105b5d51f9a" +], +[ +"cte_facts", +"45aa53c7771fd78ab16e069c" +], +[ +"cte_facts", +"975661bbfd7197055870311d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"54d072cbee2e7a4fba46edce" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"49d45df284d296a56ee65e4a" +], +[ +"cte_facts", +"ef309f48e3ec31f900a089ae" +], +[ +"cte_facts", +"06ef714b8645fe110ef354e5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"85932829e1eba29f55f85fa1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d4bf62c734315405890a719c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"2fcd2b715aee970b09d1939b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"176236cffb1d1348a8a95a3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1950f85d661f38d00a43a072" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"709313942a21ac3635ddebef" +], +[ +"cte_facts", +"522dc172f9dee3ff63e77523" +], +[ +"cte_facts", +"304123802323a41033dc1109" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"79ee6c9aa5f6c5464f47eaf5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0e0c22543052bcba25c24ee8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"42c5894e3fac7417bafdb06f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d25d576e8c29384d6e3a951b" +], +[ +"cte_facts", +"282e58a5cab310f65dc89256" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b30b3a5b8206262570b2279f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b8ab4d3fb1d826e488cd167c" +], +[ +"cte_facts", +"40e0586d9cb8dc767730f329" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"adc7bf939011845b2892a089" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e41164db8ce57be3c32429cb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"ca056830e31331e3ff379150" +], +[ +"cte_facts", +"0e878e41481a463a59693be5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ece79de0c40038457c065b8d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bb59180ccec9e3f77871df73" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"edc5dc5be882183f5e9a6627" +], +[ +"cte_facts", +"a9d09f68525851448df1619d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d11e73177a45c2003416219a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"80607bae97f5cab985a0add2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fa5292e62800a8b353f52ffb" +], +[ +"cte_facts", +"79ef7772d1f5797b348e8b43" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0fcd016def2c6a87bd549112" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c733fbd97f11c1ae39ee072" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c0ddac7e337047910d4af364" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fca2e9545f438f2926e0956a" +], +[ +"cte_facts", +"6a367d1121b68654c9b81b8f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"28517cfe8279da271d002c07" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8f70825238c1aed0ef3c5734" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"19cbe6cae615b8225b4d1911" +], +[ +"cte_facts", +"998e0045327d41c7ce1c7fc2" +], +[ +"cte_facts", +"3853a58d03252066f405f1b7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9985655040a4cc43bcf546ab" +], +[ +"cte_facts", +"ca986628c7bc0136a33f2e8b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b8b59942cb1ea5085385db68" +], +[ +"cte_facts", +"4491c04a3ad474c8909c281a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f1477a690868e13f2bb9eb6e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"617cda2ff1739604ce791140" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"de84f9b1f0d7f195a575ec17" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6bf16ad094d49e4ff755aaa5" +], +[ +"cte_facts", +"0fcd016def2c6a87bd549112" +], +[ +"cte_facts", +"77f2eda17db6070726ecd3e8" +], +[ +"cte_facts", +"09c76537f00d6996193e4023" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"866c00a7464f747e4fc1a355" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"652c0d0472b4a3a0f3893a4a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"79c3fbfd20423f5f440db2f6" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"71e612beaa6828ce304a8265" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"39857f46f51874c34b627875" +], +[ +"cte_facts", +"304123802323a41033dc1109" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dc38281fff03583f84500a85" +], +[ +"cte_facts", +"7cd5968bd2431af05f1bb764" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"92813c0cf1f3a3e54ede7bea" +], +[ +"cte_facts", +"75c3670efc226426287b4420" +], +[ +"cte_facts", +"9c2fdc6b3cc80466ffe530e6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b12b8f8d5bf1fe6b9b4e1d88" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"17bd4ac3f3b32158337513f4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"db82ebef83acd2dba14fa0bb" +], +[ +"cte_facts", +"c88e2c848da46750a3e10090" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4017732f4172dc299af21bf9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c8e8c3407ac5fa66155815df" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f44562e2016b7337866b3ef1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"00523d6cc539a1a45f58e449" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c0100d022bf48772483a6a15" +], +[ +"cte_facts", +"a3efb9ef0986176c3b09dfcc" +], +[ +"cte_facts", +"8507cf1c529a106f2e13fffe" +], +[ +"cte_facts", +"22ff3cc6b2af9a9ca87ca53d" +], +[ +"cte_facts", +"da73f43053e1e067e3c27d95" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2332482f0d5470c46bfd4320" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f14aeb66f78e1e1c707427a9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3ed17b7fa9332ade3e4213cb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45700208396b1cc8351ee18f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"4137400d4873c2f19e62d629" +], +[ +"cte_facts", +"5c61a4fa431cdc1b6096de56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f61223afe8f99d968451e5e9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d0f30baa34da1a1897488afa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38a3fce16527519e5a47d4dc" +], +[ +"cte_facts", +"b1732c1ea755b9cde57997c8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"60fab6cc07969ac9351ddd38" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"198721712d2f8ef86f7e7fda" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f6909d24c4f7453b984e0847" +], +[ +"cte_facts", +"6919dea74d6ef4c9952c4629" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3bd45518b09712347a2ef45c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"80651e94f0605a416f252251" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2572be21f7b70b9003611f53" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2ee7dac611a2ef598b6cb05c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"34e8f5e18a0953c970faed46" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"f9385aed33447ee5749935f6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"176236cffb1d1348a8a95a3a" +], +[ +"cte_facts", +"22b7dad8df2a84eae1fa80ce" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0d7acbcef647c47fd41868cd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cf56d985716a981f1117d243" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"06747f8f125a6f8f60beccd5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4f5c613de86bf7ba685e1b38" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a186e62463a75d56fa99a168" +], +[ +"cte_facts", +"3572a77a1b6c5d56b6d4c0a2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f44562e2016b7337866b3ef1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4e20b482b2ca8381e7722dde" +], +[ +"cte_facts", +"9d234bb409046114d928a3e7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"167cae4760889d1a7b691444" +], +[ +"cte_facts", +"2b48a26a0968f24175be90da" +], +[ +"cte_facts", +"91847bbf302640893ce7091e" +], +[ +"cte_facts", +"15396ac51ec2a87b7975b2ee" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b062cad1c498245f389680c1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6acb5d46486abbd86d07a32c" +], +[ +"cte_facts", +"837fded28e2525151065e19a" +], +[ +"cte_facts", +"0f7737b7a085356138660a00" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"18f165bb966172cd19eb3778" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"7c6bcd67b4bd5b7206a081a1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a722e7515ebbc0b544e0d61" +], +[ +"cte_facts", +"3fb71d1a8bebc35dc1f86037" +], +[ +"cte_facts", +"3f139d2b797bcafcb656f602" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"094711ab815a9f9abb2cef04" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c6589cffd6b1797e0f3cee11" +], +[ +"cte_facts", +"52c1bab5e329e40ca8063b3c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2ecfe5fe67d5eb423bdb20a7" +], +[ +"cte_facts", +"0f6959e2f022a11fb32e924b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c3a69d2114f4559690cd99c0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"41dfcb3ed1ab08f365780741" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"86c1fc030301d50a25902f1a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c42a804678d240d555de69a" +], +[ +"cte_facts", +"40ff1d40e1c275b97610c47e" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"af42f471da9339f89a57c51c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"51aa4ca0bdb7122fcf5b1715" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f32994d63a63eb3412200e37" +], +[ +"cte_facts", +"7b4ccafa93b90812e60756a4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3f13ff71cb4f7715d34a734a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4cbf182f05bad1387d70beb0" +], +[ +"cte_facts", +"90a6b973affaf7794e8c4f36" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"482239c9930c7cf20692a4f2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c620f3eeda4d73be333a395c" +], +[ +"cte_facts", +"5b750c06c17c5daeb14c197b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3f2fb8522e8878a3b1475097" +], +[ +"cte_facts", +"a062ba15f5478b4307d16fff" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8a87961dd00f7d91b9d4903f" +], +[ +"cte_facts", +"176236cffb1d1348a8a95a3a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"42320d623165cc351e15e022" +], +[ +"cte_facts", +"5aa160dbc9b6f9229c08eee4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"724b0a1e76206495b83adad1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e11249b1be1dfcf4994830e3" +], +[ +"cte_facts", +"105520f8ba54d96da7805226" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ece6d02b7c31182b588e0e9c" +], +[ +"cte_facts", +"b450c7e0522841b10504c038" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6590a6a60dd0c4088715e55f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5476567567fe27022c69294d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f0c0806da842233f660f5b23" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45600a76b057f3bc8d7aa623" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a72c743c7c2bae036ed3fdd5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b1876c9a7d136ec05b20875d" +], +[ +"cte_facts", +"cb1e3c176eaff984ca80e2a0" +], +[ +"cte_facts", +"27f1ee0b6b0b8de1cf296f8b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2e1d5902a5a38aced28f0ac2" +], +[ +"cte_facts", +"649fc2938efd41829c6718b9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4723ea834d37c8c25e26490d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"52c7ecf5d0e95a86e0777083" +], +[ +"cte_facts", +"57356413266917a0e7f9b859" +], +[ +"cte_facts", +"a35b720bbd7c384685d84ab8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dbe31d25e4ff467fb8099b7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"717f9807521044ccd494c02b" +], +[ +"cte_facts", +"44c669d3aae250d5b9951a33" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"82abceeb231f4799e7f41964" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1dec64953bee4bdce4b97fe3" +], +[ +"cte_facts", +"b610899b0bee8217d42ad037" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f69d3f9b13b8fdfb1632a79a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f65d89afc9299f5382043cca" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"7e8f4301e581327def7a580c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"749b1c663334dc30a879d996" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"4645d690fc914eee6ae7d048" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a0c84be8f7df1fa0f886f33e" +], +[ +"cte_facts", +"2fe95bcd33db9c87de6f84ac" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"b72b6640cb67f64a3783938e" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b40a9896c881b9b1e938bd0c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bdbab1d9d0daed776a52f047" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9db6b400c08d41ff6ffa8fa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cc5655212a098199b71e7449" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"6d155123816cd5295ad9d571" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"757a1f33485495f0e52ad63c" +], +[ +"cte_facts", +"f6b1021085276b102cfce8af" +], +[ +"cte_facts", +"92a174cbcd73b25dd952e30d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"9dea54f35af551f8c6ee5968" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6322aaddede94778335eb00a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"555ffa18ace28a83825157c9" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"fccb554a9c592d0fadee0346" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"20b6d78991ebd21f46f723fe" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"68a7603c70c03fcd659a0611" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6dd3532c9e14bc53690ae9c9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bdcd0ea2bb1049b7505a5ece" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6d5deb36bdc1757d495e495d" +], +[ +"cte_facts", +"eb48fafd626c55af4b87bd06" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e176ee352799421a8143b4d" +], +[ +"cte_facts", +"2be2584746d3dac4c3889c23" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a3c892e417a8e2434de6cc55" +], +[ +"cte_facts", +"45600a76b057f3bc8d7aa623" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0008a33cdc496fb5d9ca058d" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"28087d08814f48b361b6de28" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c8618e68970fbaae78ffd442" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c7720fc8f445f61bcb6ac336" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f6e05c8fa07d9c5010df7ec9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"67f73487af6cd1ee11fbbff3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"efff1da58173ea8714b21c43" +], +[ +"cte_facts", +"6fbf0d34cf84c348aeb42ed4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4d04d887fd5749253bfdccc9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0a954bba38ae2080b88d6921" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1dc7242a51ed6594b30fdc78" +], +[ +"cte_facts", +"efa1946f3d8f60be2b34c8fc" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7b004bd7635995d8e178b8d" +], +[ +"cte_facts", +"230187b1abce8a1b559f84c7" +], +[ +"cte_facts", +"2e8bfc93f583fb4c66145e1a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2b6ec43a6adb05e1070a1a67" +], +[ +"cte_facts", +"8213eaf4cdd52bbed31c0402" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ff81d2718a9358e6d11a7d17" +], +[ +"cte_facts", +"7c388fd67abc2f7ecacdf320" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"31ce05446f06237043d47233" +], +[ +"cte_facts", +"417e56016d81d2a2b9e54978" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9814f167c5e690ff5393c592" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7c0a20dbdfe9c498722ece6c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e0433020deea53816ed5d589" +], +[ +"cte_facts", +"10dd65ded5d2a613d7e76869" +], +[ +"cte_facts", +"7d08e8cc7252a21881b0a06a" +], +[ +"cte_facts", +"884ef8337bb248e6a85d0323" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5fda458df92dcb35646f7c1e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7cdf221d2716922f37239061" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"2551f80846c76cb4ebc6ee59" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dc5b9a03ad06b0e83d3850ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a2bf0ba96f291766da4b8231" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"33067b71e6d313793b357817" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c454ed3c77155f7e0ffea0e0" +], +[ +"cte_facts", +"30eb40553c58070860089625" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"4d2ac2ea950a76c76d323aa2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"18e92e7285d57a10ffedff53" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"10939e2c68c391793bc15845" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1d630bc706f2b85b48524f89" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"283a220696a9ea82e6aa2bf3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55661e8c9a1cb90749113cb6" +], +[ +"cte_facts", +"a4bf342d0c9e6b5cd745b698" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1adb2f87f940f4c5fd062ec7" +], +[ +"cte_facts", +"5ccd07c548e45a47210b7eab" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"f4faaf760631624f1129378c" +], +[ +"cte_facts", +"29f51d8d2d25b4cf4a10192c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fa8c09959f2b4abb8fc39d75" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45bde05dfa3b204fb9c3c267" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2978b1d68f26c464a33be3f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d6fe3bb05b1e42103ec84689" +], +[ +"cte_facts", +"ce1fda3f641433a4a8a20fec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9ee1c182c8ce3fa5d197571f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"1b4d494122e075d8b4aa86ca" +], +[ +"cte_facts", +"b62fe8bcefa2fae4625b239e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"12669194c8d2fa9b4830aab4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d81e5a2a7c847b1d584deb76" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d873d767a5240e8320a9f3c1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"98997267dc7829329c88863a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"04ec651262cc8a34f8da48d2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0007f8aed5dac22287f82f14" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4905c2d2e2cfda3f3cd3de86" +], +[ +"cte_facts", +"6883af43657eead1388ea976" +], +[ +"cte_facts", +"0178c92bc3e95130d8325935" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"49609676b34689ddbe6c8808" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da08b72d11b0a80a8495de2f" +], +[ +"cte_facts", +"51a24379b83c83a2e69994fc" +], +[ +"cte_facts", +"67c08c0d73340f1f782a02ac" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"67f73487af6cd1ee11fbbff3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e4ee03c96282047db6c36058" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e7de0bf2dccfa04e1e958140" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2222724cc55247891ba41d85" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b71001a6ec20631f3665dc45" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b1a6056040f2c34e6b3f1f7f" +], +[ +"cte_facts", +"32210938ba6d3e75a6aceb8e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"47703e9840821a5ebfa8d277" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3272c956ea97dcdfbbbab306" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da08b72d11b0a80a8495de2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1feaf21815c57c30beebd634" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7f8818c0fcf334491f14c81e" +], +[ +"cte_facts", +"6322aaddede94778335eb00a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2ce8d910c48b794c490efb74" +], +[ +"cte_facts", +"da8d9d78a475d9d818df1693" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ca804955997d73ca6f3fb2f" +], +[ +"cte_facts", +"1d165c3bda32e7efa4df9d2d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"46b4b6d25d42544f2e7bf6ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"290c1ea2572e98dc857570bb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"31b39c205d3312db2725ba07" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"304123802323a41033dc1109" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3aaeb75fc3b1cef6572146b1" +], +[ +"cte_facts", +"f9ba2c1daedeb60ad9376773" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"46b4b6d25d42544f2e7bf6ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"037d6ccdd1fcea617040ed61" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55aa6edc7136de96533893b8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b4498848a12664c38a13bf96" +], +[ +"cte_facts", +"ed1e41d85d2509124c7b456b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ca6551b0fec7e33588062815" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f95a77a0e7de4f31d0137f99" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"963f959d14589a1a8f2c87b5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d140395495d0968003787b7d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"4267c6ef3c4b0e04fc1f534d" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"016ec924ccc4489fe8878cda" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b975abf2d08ab0b0205feb69" +], +[ +"cte_facts", +"3fe1087dfdb455dde4653246" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"10b859f1a678eec1c34a0cc5" +], +[ +"cte_facts", +"3cb41141daa9ddd0d74f3729" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"418e76271ad967ca78780e1e" +], +[ +"cte_facts", +"da39cb61a8156b9d35e40ff8" +], +[ +"cte_facts", +"51e237dd90a604a3b3f3aa19" +], +[ +"cte_facts", +"8500a483f8df24f38c8c23d2" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"81624485f5514e17f503f5c3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b196d92b38e52754a254a1b1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5d0be0066c79e2eec970fd7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5917fcd7440f8573b4ab68ca" +], +[ +"cte_facts", +"040a05a204cc7cbc4bd535b8" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"3f5a1a74656e9d7762bf1362" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f253b49d79c557c338e6307e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a38235e266f041741142a40d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"00523d6cc539a1a45f58e449" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2a0fb1c4f88fbf544e9183de" +], +[ +"cte_facts", +"5bc37cccece14cde52342bc6" +], +[ +"cte_facts", +"7e0536f9e5b2b98d26a56e04" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4953cb13b3d9baeefe2615a1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"be4c9788dc924b3100840d0f" +], +[ +"cte_facts", +"249f7e5f71a39006f3c14a72" +], +[ +"cte_facts", +"acc36cd08c12fa39661bea65" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"82cd724e966b57061496224e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"088c7ce81d5775c768596f66" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ec25592a7d409e081b6b784c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c3a69d2114f4559690cd99c0" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"b3d9fdd96920c3c9ef12c652" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"969be68816a2bf237912467d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7fce065b3e64611d802cf333" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c701bbcd7815869a9a8594d0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fcdaa38c3809a7d32a1bd2b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"53d5f34211bf506d07b1f2eb" +], +[ +"cte_facts", +"f852138658c425bd9a006b1a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ba4713328c180e6ad5d2407a" +], +[ +"cte_facts", +"474a269e5f95b8ea76d81ca5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dc08d1c4bf866bf308b3fa98" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b39cfe621a79645d55a4771d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a9df1451df001f07290aa075" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"dc08d1c4bf866bf308b3fa98" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"53c504b91c72b3153ddd59ad" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"66b45459fb385666c9ff99e0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"95f07e40c09dfade0e42f5f9" +], +[ +"cte_facts", +"46b4b6d25d42544f2e7bf6ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1ae691e5459e8fb2d011da84" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e19909d2d575ac3a9a125712" +], +[ +"cte_facts", +"c8e192f83a60d696d37209fc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e1c48991009118f93f7baf3b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4267c6ef3c4b0e04fc1f534d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"383b4dda57032b52cb30c912" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2993f996e60f7066a938f79e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d8265cc727a41d136697ac89" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2878549a7d60ce3431c19f45" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2b6ec43a6adb05e1070a1a67" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0e878e41481a463a59693be5" +], +[ +"cte_facts", +"cb9f05ef54d8018793a75f56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ff839717362d149880c0fb24" +], +[ +"cte_facts", +"251da44f07a6718b8b46dc5c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"41170c7e7f50b26cd532e977" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"4b0b4c079d65284cbf57769e" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"67f73487af6cd1ee11fbbff3" +], +[ +"cte_facts", +"eb56f580419df376b3679abb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f62a7b127ec0ec8d98a625c5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"324799a36510203a6f2c6af3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"f3db0ea9dc43c0c11f041a43" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"151bb12e03386bade0488e59" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"91b036bb4d0c755410bb7dce" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a158d664e26aef086fdd9a38" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f32798d859a5c7979ecba6d6" +], +[ +"cte_facts", +"b21a415513d11154cd105b61" +], +[ +"cte_facts", +"3732b652976d866f62ec1395" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a217e4c6047b1b194a54ed72" +], +[ +"cte_facts", +"c8fc0cb813f1bd1a3b12468a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8e842a0c0b1a5901617d182f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2457ecd54476f010c09ca4ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eed09387c738f440df36ea2f" +], +[ +"cte_facts", +"717f9807521044ccd494c02b" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7b5e9f6e6f5ec8ec244738e6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c243e31a2befa203a6154650" +], +[ +"cte_facts", +"c2f38e187b15ec634a125aad" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a00bc1aea089f5badf154464" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4807fe43c31af8238626fe39" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"683338c7522beb9c05a72612" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"21f4e89cc35d9e8b7f3bfe63" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3274f33301a6a3c97000363f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3703fddae1b50fc130eafd01" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f0640e958bba25c48aad0984" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"17ee5b26084855e33f69240c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3fe437233e76b32c82aa4960" +], +[ +"cte_facts", +"ea7d43dc213da833dc086914" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f31f9eca9b04d25eff8b5df1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"243fd67a323d23e67fd3db2b" +], +[ +"cte_facts", +"fadc71852c5c0ab7b5169281" +], +[ +"cte_facts", +"26481ce75c005203b1413d01" +], +[ +"cte_facts", +"7a596b24bb047c6e03732ed5" +], +[ +"cte_facts", +"db05336e2f04f398af8bff82" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a6bb709b6e4d2e86603784c4" +], +[ +"cte_facts", +"4c16e56339db6dd62df77424" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1d0aefb742d3cc3ccdf41167" +], +[ +"cte_facts", +"d0c2eac7fb0bea36376d8aca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e57fa2019527d5da7b74ec9d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a748c19ed802f3646685c6cd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"07c7150255f0fcaf9dc5fd4e" +], +[ +"cte_facts", +"fbdc8fece530908241600a61" +], +[ +"cte_facts", +"71be1b331269611775b39569" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ad7975b1c11568771ba44d1f" +], +[ +"cte_facts", +"197a57c57abf0ce6d8117ae4" +], +[ +"cte_facts", +"2ac93f229c5c55a83a2d4e1e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38de0f2076daf1d1cdf8ca12" +], +[ +"cte_facts", +"99962340aae102b749214f5b" +], +[ +"cte_facts", +"c9e03f7293c494b468ea3a84" +], +[ +"cte_facts", +"ddc453c4206a43050d3b6049" +], +[ +"cte_facts", +"d11e73177a45c2003416219a" +], +[ +"cte_facts", +"fd8b37a35f3e2c74c92576d8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2b48327eac2857de28723a8b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a1790d109eb9cc7ac8442c6b" +], +[ +"cte_facts", +"aff94128a9a6a9d58ea8c9d3" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"4829c1b6749ee3f0daf22aaf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"deeceb5570e18756f333224c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"285a14299a30379b36f18e59" +], +[ +"cte_facts", +"7ea5b68fca19d8e61031fdd4" +], +[ +"cte_facts", +"6acb5d46486abbd86d07a32c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5151c7461586a79a23178305" +], +[ +"cte_facts", +"5e40ec0e62c3718d4caa7b27" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f94231934a8c3e86f70abd3d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5818c802e60b73c5a7eb5777" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"82091a3a728cc1417e3e5ae3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ea6861c3d537c45e738b2efa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d05000ea3b06a6932c017289" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"84ea798721e6c47aeb14f586" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9a6252d8bd4fc1316ccaab48" +], +[ +"cte_facts", +"2dd3f3a5cfebb64437827eac" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a69d279fffaa9ae58a8addbb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ce3293dc971e498033109aff" +], +[ +"cte_facts", +"9814f167c5e690ff5393c592" +], +[ +"cte_facts", +"975661bbfd7197055870311d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"95ca4e21e5a247437ebbddd2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"07c7150255f0fcaf9dc5fd4e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5ac96140ae9c682b95d52c2c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"76591a1a1f8a63dca0278c50" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"68ca358ec525a5ffb3c4cd35" +], +[ +"cte_facts", +"ea42d50b24e83414e92ca8a7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4db4ac00559e311fab7826c9" +], +[ +"cte_facts", +"f44562e2016b7337866b3ef1" +], +[ +"cte_facts", +"62309c4535558a08999e41d7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"25f08f5c7c074952b086c536" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"879e8fa5ec50584af1c5ef12" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"c8d747bb19cb77e6ffda9003" +], +[ +"cte_facts", +"af3e2f1452177aa202453ffe" +], +[ +"cte_facts", +"3379a101945f58072771f412" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"72e789c2a0764633c8f1e636" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"76b78e9fbe01664ac682d010" +], +[ +"cte_facts", +"fe330670ca51793280992f7d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ca61dedd06208f13b0e129e" +], +[ +"cte_facts", +"537f955a1bdf530fef4d39c9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cbb025e2b4148912c45027b1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"436b014b14265d8019841d73" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"970ab5a29b026b200a202a7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a9bd0e3ae152fc636e8ef5af" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0271725c92598880b265f021" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"98669ad166dbfc3dfbe09e2f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"da2c7ec20946232109d43b7d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9ca4644f22b8c74ee3797803" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7834d8a101237abd8f35634b" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"612d5180f2d9e383994af37a" +], +[ +"cte_facts", +"5bc3260ed7f963f7a4694635" +], +[ +"cte_facts", +"b5e27d6d5aee44223428a6be" +], +[ +"cte_facts", +"a20c63c9a459df679beec7b7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7c156b40c6ac7f1176e795d6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"d66b6d0428a6f95d8318c53a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2942869bb9e3f82842befdcf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0e878e41481a463a59693be5" +], +[ +"cte_facts", +"4ee2a5c08d13528a237d7b53" +], +[ +"cte_facts", +"73d16bf6421a7e278ce66baa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fce25a5297f52605b9efe6d7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"32352f8b74230942bbc5bb4c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ca61dedd06208f13b0e129e" +], +[ +"cte_facts", +"cbafa1982d2fabe42cab90bc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0207c19e8f59719855a52afd" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"fdbaa125d44c45819eccd00b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9b2b965b7876af21160f7287" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20b9f444a1efbd5329c078db" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c60a8e5fd02b52099cfe02d0" +], +[ +"cte_facts", +"18c6ccce22456faf6e6481b0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a15a6c8b36eccf68bd084a71" +], +[ +"cte_facts", +"2ddd610b20ffdb4fb6b23729" +], +[ +"cte_facts", +"9bdfc40f10bdfcde4358032b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"310686d1164e8e4c36b2a716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e616619423cd5f6eb3bf3f7c" +], +[ +"cte_facts", +"20e56d455ffb35f8a4fc392b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"975661bbfd7197055870311d" +], +[ +"cte_facts", +"21c6723982eb517d413a2f0d" +], +[ +"cte_facts", +"47ce4c603b269637acffc231" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ff94ee1b456022577fb27f97" +], +[ +"cte_facts", +"600bf5e5cf97ffeac7a142ef" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"893f0c62ce53a72d332ac0bf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d30777b977850bbb40133d05" +], +[ +"cte_facts", +"f65699a7947784a10af4d56f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7816cb41efefaf9a53582088" +], +[ +"cte_facts", +"134f1f18ae356ecff89ffa05" +], +[ +"cte_facts", +"e550fe99452f9369374a43aa" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2fcd2b715aee970b09d1939b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4074edd4c9844c8f0464bb10" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c98042e640b64e0213124d44" +], +[ +"cte_facts", +"5a453cd7b93ed7b68e6f8de8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"043656cc4910ccf8996cd6f9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"823cd0f2ed932642009878a5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4ed55662a7fd257c4580d5db" +], +[ +"cte_facts", +"2581d3f5dad6a8fd1d2a59c1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1ff6aff6138509d87ef388c0" +], +[ +"cte_facts", +"4829c1b6749ee3f0daf22aaf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cc21305ff6f775769ffe22a0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4645d690fc914eee6ae7d048" +], +[ +"cte_facts", +"e163bc8972fd78f3cbedf2e0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ccb221a0d0f2af9addd4009" +], +[ +"cte_facts", +"2c713820741de9d6d39db0f9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5705f459c4fef601e9de2216" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8b42dbc662c729fdf5311853" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a186e62463a75d56fa99a168" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"627bab400810971b05ee7dee" +], +[ +"cte_facts", +"7f289dd18993c29e40469dee" +], +[ +"cte_facts", +"99962340aae102b749214f5b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e228b36c9c39623e981b109a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d6f2fa2e0bdf33cb03a885df" +], +[ +"cte_facts", +"3c733fbd97f11c1ae39ee072" +], +[ +"cte_facts", +"4d2ac2ea950a76c76d323aa2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"78fc47b18cd2e829e476963f" +], +[ +"cte_facts", +"1e0b5081e91e76f874870f56" +], +[ +"cte_facts", +"7d3e2e6d18d037714f03b362" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"304123802323a41033dc1109" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"754bb30a1a568f1bb9cd2b9f" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bff0842055947ae5ef20e050" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"aa0cffa33cea460d3abb6f82" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"98bec2974d565eb8da2d52f2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"3f7d100dc2e33a2295ed892c" +], +[ +"cte_facts", +"da08b72d11b0a80a8495de2f" +], +[ +"cte_facts", +"d8d7d9b0b50847f0e16c04a5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8b004a796c5ec4af837cdc99" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b517891860773a735a936b11" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"2971230791a89beacb266ca5" +], +[ +"cte_facts", +"105e18d72cafb88c3bba073d" +], +[ +"cte_facts", +"2de3863e3e3b27acd68bcd5e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3732b652976d866f62ec1395" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fa78956a78e531f7ba4f6bbe" +], +[ +"cte_facts", +"8dbae9c6daee5c71e110ecf9" +], +[ +"cte_facts", +"5abe465025f727c471fa8a32" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0d9a8d99fa86f5318ebd0b51" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bf912cb617cf5276d3f3cb69" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fd7c2d795a3033d55c9fe53e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8e8a2e7778d6ceeb65c93208" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a428c8b7529d336b14a92aff" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eb44901cb5bef8a9623eb440" +], +[ +"cte_facts", +"142e5a445d063f8072735675" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"de23276a41fca52983fca6e9" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38a3fce16527519e5a47d4dc" +], +[ +"cte_facts", +"5d01e1fe272632e24389e00f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d493e2bde56595f2f45c5620" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9e48f5c1ca7472eec267dc40" +], +[ +"cte_facts", +"b1d48ff3f4e22895f2372695" +], +[ +"cte_facts", +"1ea34dd04bc4230cd86dcc59" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"627e639dcd1624abbf32acca" +], +[ +"cte_facts", +"9b10a032e998f9b508453e2e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a5ccf830e534696f595a3ac7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d18c9d48be2a96845523817b" +], +[ +"cte_facts", +"6f967aed45f75589baec1064" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"ded7cbfa470e34d049eb6847" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2179e2ce4ce9db4c1409b8e7" +], +[ +"cte_facts", +"c6b992be63a0a1341c2789de" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"56a2c31ae98e78ad0153970c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"877a40768913bceb77234a09" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6322aaddede94778335eb00a" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"e228b36c9c39623e981b109a" +], +[ +"cte_facts", +"3d6d751da31e7fecf614f50a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fa78956a78e531f7ba4f6bbe" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"fe960f3513b186a69e6db3bc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bc5fc94739b12157ed94f2d0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a51d44f88302c135b7f40232" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1bfa15e991eb93ee9aeadc37" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"25c49563553f4b37747919ae" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"687a1a7ceab548f637b5bfc6" +], +[ +"cte_facts", +"a00bc1aea089f5badf154464" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"52232f43fbe850c6672e95a7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ee2f1e6c32c94d1161ac3181" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9930623e1aee198195f1d0fe" +], +[ +"cte_facts", +"f10c2e7703b93cfa2a7c2470" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"63f1f83c774d21323b822d63" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"01b18a813c0e139f7141d489" +], +[ +"cte_facts", +"37f4f889dd27c4d2715a0f43" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ef602f1b1f3491ef6b896357" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ce9bf1a58898a0b054656e4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6bcf69afe466a8a9751e9ded" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"370d84396f619d4bc85b671e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f56d783c937245b628fbe0e4" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1c5747d7ca108e739c99e68b" +], +[ +"cte_facts", +"9ff66d22a8237eb258b3e02b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c321e0b7baae09e495a97ded" +], +[ +"cte_facts", +"46e612be9b2d5c332f91c2fe" +], +[ +"cte_facts", +"7c2c000285de82df6d7c66d1" +], +[ +"cte_facts", +"f9d884f02c33e38ffa6ac8e7" +], +[ +"cte_facts", +"0acaddaf873d067a844a7a56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"51adb7a7b43a2e77762636fe" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"af3195b0c034a44bd2bdd812" +], +[ +"cte_facts", +"467e6e2db71288e4a594cc3f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"06ef714b8645fe110ef354e5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"a186e62463a75d56fa99a168" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1ff578ebe02377c97036fe6a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"36d8a83a3563b286923195ac" +], +[ +"cte_facts", +"91120486c67447cb5dca6723" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ecf7f2b6754dddfcf690a8f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bacdeb92b7c2a043b4d54896" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bac2cce5147fa305f14f21cd" +], +[ +"cte_facts", +"83c05f9bcabc42c474badf6b" +], +[ +"cte_facts", +"bf631a28a36fdf54a225c864" +], +[ +"cte_facts", +"ea7d43dc213da833dc086914" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0ef179b9a0178ae379429e5a" +], +[ +"cte_facts", +"8b0096aea967f3de921be026" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cc2a0d6e14869bc62792bb26" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"72050c12500b770ad291701c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8871f19bfb0b4e95225018ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c671c33396672c9c79db48c4" +], +[ +"cte_facts", +"4575b1c93f423910102d880c" +], +[ +"cte_facts", +"afbf1b83e47396a0be837435" +], +[ +"cte_facts", +"46a25e730d1bdf56fb0a014b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"56ab0c91556976b203ddd1e2" +], +[ +"cte_facts", +"38f4b4ac40110dd5d6c43ac3" +], +[ +"cte_facts", +"612d5180f2d9e383994af37a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5cd1fc8c032b6c627687105d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"67f73487af6cd1ee11fbbff3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"10e8e48c05a85795eb1c5044" +], +[ +"cte_facts", +"c4092e7706a7e6ed9ddf2b81" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"797e9c6250ec1d5640117d48" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"be1dd96ced3ebd1b1877244d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2aa652bf2cfec1da7f8c1173" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c6fbf3e94d78ae117161fd19" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9ff66d22a8237eb258b3e02b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38f4b4ac40110dd5d6c43ac3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"80fa83935ddd8f6b08eeeb7a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"d24996d97ff4fef546bc3724" +], +[ +"cte_facts", +"bee38be89606bf949435caed" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0a364c624111090c04f021a2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5b2d31a14b17341b0a949eb6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"be1dd96ced3ebd1b1877244d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"942040d62815f7336996f1af" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"10c4206e33f15bf86e1370fd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7f8818c0fcf334491f14c81e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2a959c85bd742066eee11c5c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"292377d372fbc3dc3bb81492" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"ed3848ce1b9ff99a1ca19c0a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"755b369d1abd98c0cb9b83ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d1a10a5283331fd270bbffc9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eacad644b7889b7c54bf9840" +], +[ +"cte_facts", +"ea7d43dc213da833dc086914" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"88c909edfc66dc4271930356" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ce3bf2dbfc7032bb0ce56b9f" +], +[ +"cte_facts", +"b8a677090052ffe5dfa0aa0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"48696258c7ec779e1ea036ae" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6ff06232aabb72d368cfeae1" +], +[ +"cte_facts", +"3a9bf5cc8ff8bb0b49f2413b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"968ac983ec3cc08095608953" +], +[ +"cte_facts", +"847aea89c4a3335728e3c612" +], +[ +"cte_facts", +"e8e5161a6f481ab44ae68a45" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"91b01cb7215d541f75cea4b8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d2878b450de47bb786eb0c96" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"eab186d2e7e5c4848d00d629" +], +[ +"cte_facts", +"55272ddc4f2bf5ab0526db7e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b40d732b81585f13ec3a22b3" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d11e73177a45c2003416219a" +], +[ +"cte_facts", +"4d6759b4b9808c6022a18ea2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ab94b1c6244c902eb67db59" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"eb3f6e1b0c45a4aa7d0a398e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9582bf20f07771c62e78b769" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"98c52349b67d23dfb2848c40" +], +[ +"cte_facts", +"6bd9bc7c2c16689ddd8ea738" +], +[ +"cte_facts", +"fb5a8619439d2baf467531b8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ce62a882c7bbc0eb5852bbcf" +], +[ +"cte_facts", +"b82d76b3de38f603cf2b0526" +], +[ +"cte_facts", +"fa6d8a8b325e116a0d5d829c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e05d64f215bd4e41bbeee00b" +], +[ +"cte_facts", +"e42ef49172e3dfa8ea8015ed" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1e0b5081e91e76f874870f56" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9bbe2e359e8edf8fea763fc7" +], +[ +"cte_facts", +"8ca61dedd06208f13b0e129e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5978d6eb10ab9e14421e1f71" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e9290211930fee5e99fd9acb" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3df33633ea4e2783889aa339" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f99a8a4b00f1bae2ca8f39e5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ce2acfe9bb7ea1362545d17" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ea96e238b5198477bd30504e" +], +[ +"cte_facts", +"3201b6eeeef7c538f171dce2" +], +[ +"cte_facts", +"0a62ff40d4f9fe56c5359f2c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"8ae6c233d1d58a1ae685d56c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"442fabe01e432e3177422456" +], +[ +"cte_facts", +"928f47456cdfcdc751cccd31" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e06900147c8f31d9c37b1209" +], +[ +"cte_facts", +"879e8fa5ec50584af1c5ef12" +], +[ +"cte_facts", +"0fbff11a946f4a2ec01abb40" +], +[ +"cte_facts", +"80b147aeadb9d07f6b981647" +], +[ +"cte_facts", +"e3ad81de9f1209b7d2ef35ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9e8c7c69884949b723e2360d" +], +[ +"cte_facts", +"471ae877a2e3fd13c43f6a39" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f718dd96e91014fd7962bda5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7d4315b47d9135f05277c1fc" +], +[ +"cte_facts", +"f8c008083d9c03f03d83c8cf" +], +[ +"cte_facts", +"d037fec55eff933500ace801" +], +[ +"cte_facts", +"fd10a125a304da4065bea134" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b199df3de4c95cbc60acd0d5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6a212a0782392ac284539274" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7861944ce6f8e5598ecb504" +], +[ +"cte_facts", +"aa9fb5b8c130c6ffb432f3f8" +], +[ +"cte_facts", +"4db4546cf049f2d63cebe9f7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a9d8c38fffa45e3b0d783f" +], +[ +"cte_facts", +"25a10549d1ae01a58000cd0e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cf56d985716a981f1117d243" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"411a4ff3c72a2d03bf69579f" +], +[ +"cte_facts", +"b7a00046e5722e993b5b0e35" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"236f440b1693641ee2e5d1bd" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d756bdc130b80ac669a33f5d" +], +[ +"cte_facts", +"c3227e0d82eae5a699230286" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"bbdc27f8430ec345f2a0aa48" +], +[ +"cte_facts", +"5832d5537c11108289369e5f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a4c96acb626116be3fb32cbb" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a15a6c8b36eccf68bd084a71" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d672a1f46cae99b6118bffb9" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"ba48ca02849c994ece991e1f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"514f07ef557fc9d7f7af5a93" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"1150565952dfe907fa12740f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d45fb2c887dd774246d10f61" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a9c7c5354062238c0d6f2471" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"502bc77fdd1949aac4873b0d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"f5b6b22b1d2051b8f8cd0744" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"314073b0d335b1d62091f044" +], +[ +"cte_facts", +"96c64e0ded4ba628e1653f40" +], +[ +"cte_facts", +"d73a408b0cab64dd3e8068d7" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"25a10549d1ae01a58000cd0e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"44bec6427a56cac3c1205f3b" +], +[ +"cte_facts", +"983ed2a5345d16d2f1e3556c" +], +[ +"cte_facts", +"c01a47626a907b90a83be9b6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c10c2bea99ae7d0b0b7f8597" +], +[ +"cte_facts", +"25a10549d1ae01a58000cd0e" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7274a1810cc185851e65ce5" +], +[ +"cte_facts", +"943060f97c1b26bd5461c8f5" +], +[ +"cte_facts", +"0a4b3cbac83ebcf9c3e4d647" +], +[ +"cte_facts", +"2e4a6a1fcffeb6a8e1d15e10" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3ee0c2f34f0b58b3b5e14f93" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ad302b5c5cf18359c71c36a9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"962e7c206696722146a65acc" +], +[ +"cte_facts", +"5e98624071bd40877af25354" +], +[ +"cte_facts", +"845f2755a6571af998db7619" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b7bf1d8e6e042ae95ad5efad" +], +[ +"cte_facts", +"07c7150255f0fcaf9dc5fd4e" +], +[ +"cte_facts", +"424e3607822bb4be2e1d6e05" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c2ef1a1274c86ef5a5488140" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a94ad245364fd7872dbf4864" +], +[ +"cte_facts", +"e946d24f59d2f646bbe5c5fd" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7aa55059d621c5f3131ff31a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2bffd0749d7e8d7811bede12" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d0fc51a3fd267dd6581f737d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2de3863e3e3b27acd68bcd5e" +], +[ +"cte_facts", +"26d66c1506328beaec0f199a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c673c90e1972992cce693cb0" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c469cfefd0affbfe7ffd273e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"17fcafd3607575d317543125" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7b0577a48135bbd47c670e17" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e79441481098a255703e4e81" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c0ddac7e337047910d4af364" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"831eca53f82912733dc514d7" +], +[ +"cte_facts", +"9dd090a6429a40f2e4b6f108" +], +[ +"cte_facts", +"882da20a3b2430bec6b02456" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"881674709cec7cb115a04e78" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"762b6211fab680cc67ecca2c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5fd7bd460e3c527ef04b1099" +], +[ +"cte_facts", +"feb9235f367134393bb02e10" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d54e34db9e36522f6d471204" +], +[ +"cte_facts", +"d7614dbe582d2f103c7461a8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a145d981f53f939f824c319c" +], +[ +"cte_facts", +"879e8fa5ec50584af1c5ef12" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"d87fea57b43a56c38d7b0b05" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"07c7150255f0fcaf9dc5fd4e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"2c0f877bb4c8f707c1975ac6" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"38de13edd098e9fef798f70c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e1ff8faa5277683c5e31b40e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3c7c4024333942fa430e93d4" +], +[ +"cte_facts", +"167cf9c912e644cf4ef3daed" +], +[ +"cte_facts", +"afc2249b34136f0c630661e4" +], +[ +"cte_facts", +"6d20eb5aef6b72ea0cc8b39f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0d3614828e2f4b5c3c3ca2ba" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c8ada545dcbfb06a0a866525" +], +[ +"cte_facts", +"28382d4577b8cd305211f99c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"41f0303c8377f9b414988aef" +], +[ +"cte_facts", +"a7e34cf87ff4348a9640e6ab" +], +[ +"cte_facts", +"30fcf054a8192ae04e617319" +], +[ +"cte_facts", +"b86e85acd2d15fb34ca979c5" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4f5c613de86bf7ba685e1b38" +], +[ +"cte_facts", +"664bffc25c9f2efb1f37053e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"22c87aeb15d4844e3748362c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c521b9d1b0084aafccb77db6" +], +[ +"cte_facts", +"5cc4aa1bf51dfb23ebd34d5d" +], +[ +"cte_facts", +"1facb03191eab779eb187997" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"69eb5c3a254e812eb33df7fc" +], +[ +"cte_facts", +"e616619423cd5f6eb3bf3f7c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e9630613ae7b7c40cff99261" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"9c5fe8212f6c104e58f8a146" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"236f440b1693641ee2e5d1bd" +], +[ +"cte_facts", +"b976ce111f5bf18c67c6b490" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f301f4d6d2dab7665154bf25" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0ff36f2b49db0e4d671f2617" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"14c604fbddeb705ee2872a99" +], +[ +"cte_facts", +"8083370fa0ead4f9af8cb0b6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"45a5185979b9f820b0058626" +], +[ +"cte_facts", +"5925fee334e58d657b2b9aef" +], +[ +"cte_facts", +"be736807124339a0a1c410d8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"418e76271ad967ca78780e1e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"343133878e398b8afc9d4ec2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ec98b19df2d4b0c5b7223608" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e5c0431376b17c69ec8bb239" +], +[ +"cte_facts", +"13c6de73dd9de5ac6bf0037e" +], +[ +"cte_facts", +"c8ada545dcbfb06a0a866525" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"617ed1874f379e6a768b996e" +], +[ +"cte_facts", +"80b337a9f115298079a9bcfc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b15daac7d7a5fb4b834de84e" +], +[ +"cte_facts", +"6a0d23ddf3acd4eb8690adc1" +], +[ +"cte_facts", +"31ce05446f06237043d47233" +], +[ +"cte_facts", +"cf1168ff284299b75c57c9b1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"5430136f6bffab1727da3f78" +], +[ +"cte_facts", +"a49aa42ae820bd886c8f5796" +], +[ +"cte_facts", +"f4faaf760631624f1129378c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9b10a032e998f9b508453e2e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"80b0c8aa54f739249a1755f8" +], +[ +"cte_facts", +"df75f39d2709de69b46c11ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e00d4ad6ac1059ec06a5eabf" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"cc1270875c486a516ad0684b" +], +[ +"cte_facts", +"ba7d76432f08d918fbfcd3f4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e83331025b460b5b1db2ac0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7ae39c4f9935ae9f91b35417" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9f949d4cc5bec682e71b5f6" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6bd60dcc297125b2d33ee528" +], +[ +"cte_facts", +"b1a6056040f2c34e6b3f1f7f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c9bb60687815b7fd87ac6de7" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"785c80e17f1ac174c0151de1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"934209e73aef79ef60ea8520" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"323a2aa8aeea917ae183741e" +], +[ +"cte_facts", +"decf92afee5279adb4e9baf2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"494f8ba6c8148923e77c5fbc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e616619423cd5f6eb3bf3f7c" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"ec9e6329e8f49cff8e7ad9ec" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b4aea9555d5badeb28b80c3c" +], +[ +"cte_facts", +"e922001a12292d0640de3543" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"4377bd36fe5c13dfa27b6a62" +], +[ +"cte_facts", +"6b301515a097329dc3dea376" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"d4c14ed86311ebc54b9011a9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6b68919c26ac72cf8c5917f8" +], +[ +"cte_facts", +"2f3e7c0d66dfb4841008dc7a" +], +[ +"cte_facts", +"0ae01e775186032a46e2d7f8" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"b9ca9fb380cf29787051a4e4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"f44562e2016b7337866b3ef1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"6d030114c00b9d13ede8191f" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"20e87b1d9005062c36c16b4b" +], +[ +"cte_facts", +"abf6ef72e65b008efcc58f84" +], +[ +"cte_facts", +"6e6b4f0853902fb5771cac06" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"7df2768aaf03df896e6c8294" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"84611df4437258dad142d9d9" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"3beb8118486379f13a539b34" +], +[ +"cte_facts", +"0d972b0a013e308e6bef5433" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"165757241c36f8dbeaa6f4c7" +], +[ +"cte_facts", +"89635859e400c81e7d499fbc" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"0ccdbcb06a8ef737561da019" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"73b1effe619301b79ca31b9c" +], +[ +"cte_facts", +"eb6f02a66c40dd142124cfed" +], +[ +"cte_facts", +"e266b3adf96c73613a987818" +], +[ +"cte_facts", +"25a10549d1ae01a58000cd0e" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a9724dd337fbfc891eb7f173" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"11c86e815b4e7745ce308c15" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"02c20a1ad10b6ffce259f85a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"931c32179bb339f5390d28ca" +], +[ +"cte_facts", +"58190c870eb7e4d6f518d229" +], +[ +"cte_facts", +"d59e252be9d2d0a5fdcb7c43" +], +[ +"cte_facts", +"35310061081f1ca0a805b6fd" +], +[ +"cte_facts", +"95c878fdaff0ff89d40d4dee" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"998dea787a69416f0fd447f1" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"78908fd99fbd26b0cfd83ef2" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"e17a7646aaeb5caddd14b1f4" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"c1c38f3b91d5b4fe5437f4fe" +], +[ +"cte_facts", +"356b53eeb08ce1f1c88b3b5d" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"9ccaa670d5e0e168faf1209a" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"22b16c75c454a71a7bdb9880" +], +[ +"cte_facts", +"3530e4c6fb1da09d012c270c" +], +[ +"cte_facts", +"0ec1a6575356c3a38663a8fc" +], +[ +"cte_facts", +"3faaaadc7b7cc9efb19175cf" +], +[ +"cte_facts", +"e4d853295fe692e5c86d60dc" +], +[ +"cte_facts", +"95eed6ac5f76289b7a9b5922" +], +[ +"cte_facts", +"04278e4fa62692e93672a830" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +], +[ +"cte_facts", +"446781fa398ac9ef2b09da0b" +], +[ +"cte_facts", +"a7a3f9c643602c78a03c7716" +] +] diff --git a/tests/integration/src/sqlbuild/cli/commands/main/helpers.py b/tests/integration/src/sqlbuild/cli/commands/main/helpers.py index c1df2f1e6b..bdc073dcf6 100644 --- a/tests/integration/src/sqlbuild/cli/commands/main/helpers.py +++ b/tests/integration/src/sqlbuild/cli/commands/main/helpers.py @@ -27,8 +27,8 @@ from sqlbuild.cli.commands.classes.prepared_compile_artifacts import PreparedCompileArtifacts from sqlbuild.cli.commands.main.entrypoint.entry import main from sqlbuild.cli.compile.models import PlannedStaticSqlTests +from sqlbuild.compiler.analysis_session.models import NativeQueryColumns from sqlbuild.compiler.compile._helpers.assembly import source_bindings as source_bindings_module -from sqlbuild.compiler.compile.models import PolyglotAnalysisResult from sqlbuild.compiler.discovery._helpers.native import ( model_files as native_model_files_module, ) @@ -661,17 +661,15 @@ def record_source_rebinding_analyses(monkeypatch: pytest.MonkeyPatch) -> list[st """Record every model query analysed again against inspected source columns.""" analysed: list[str] = [] - original: Callable[..., PolyglotAnalysisResult] = ( - source_bindings_module.analyze_columns_and_lineage_with_polyglot + original: Callable[..., tuple[NativeQueryColumns, ...]] = ( + source_bindings_module.infer_native_query_columns ) - def recording(**kwargs: Any) -> PolyglotAnalysisResult: - analysed.append(str(kwargs["query_sql"])) + def recording(**kwargs: Any) -> tuple[NativeQueryColumns, ...]: + analysed.extend(query.sql for query in kwargs["queries"]) return original(**kwargs) - monkeypatch.setattr( - source_bindings_module, "analyze_columns_and_lineage_with_polyglot", recording - ) + monkeypatch.setattr(source_bindings_module, "infer_native_query_columns", recording) return analysed diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py index ec47794d45..6db0951552 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py @@ -6,10 +6,8 @@ from collections import Counter from collections.abc import Callable from dataclasses import dataclass -from typing import Any import sqlbuild._native as native_module -import sqlbuild.compiler.compile._helpers.analysis.compact as compact_analysis from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( @@ -23,13 +21,6 @@ from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter from sqlbuild.adapters.sqlserver.classes.sqlserver_adapter import SqlServerAdapter from sqlbuild.compiler.analysis_session._helpers.profile_rows import nullability_rule_rows -from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( - _polyglot_cte_passthrough_facts, - _polyglot_filtered_non_null_outputs, -) -from sqlbuild.compiler.compile.models import CteFactResolvers -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot type Generate = Callable[[random.Random, list[str], int], str] type CteFactView = ( @@ -49,10 +40,9 @@ class CteFactQuery: @dataclass class CteFactParity: - """Python's and native's recovered facts for every query native answered.""" + """Native's recovered facts for every query it answered.""" queries: list[object] - python: list[object] native: list[object] counts: Counter[str] @@ -86,20 +76,9 @@ class CteFactParity: "Events": {"event_id": "INT", "kind": "VARCHAR(10)"}, "events": {"event_id": "BIGINT", "payload": "JSON"}, } -_INPUT_NULLABILITY: dict[str, dict[str, InferredNullability]] = { - table: dict.fromkeys(columns, InferredNullability.UNKNOWN) - for table, columns in _SCHEMAS.items() -} _INPUT_SHAPES: list[tuple[str, list[tuple[str, str]]]] = [ (table, list(columns.items())) for table, columns in _SCHEMAS.items() ] -_NULL_FILTER_USE: dict[str, str] = {"context": "filter", "expressionSql": "x IS NOT NULL"} -_RESOLVERS: CteFactResolvers = CteFactResolvers( - expression_type=compact_analysis._polyglot_expression_type, - nullability=compact_analysis._infer_polyglot_nullability, - shallow_nullability=compact_analysis._infer_polyglot_shallow_nullability, - alias_nullability=compact_analysis._polyglot_alias_nullability_from_select, -) _ADAPTERS: tuple[BaseAdapter, ...] = ( DuckDbAdapter(), SnowflakeAdapter(), @@ -369,43 +348,6 @@ def generated_cte_fact_query(*, rng: random.Random) -> CteFactQuery: ) -def python_cte_facts(*, query: CteFactQuery) -> CteFactView: - """Python's recovered facts, as enrichment computes them, or what it raises.""" - - analysis: dict[str, Any] = { - "cteFacts": [1][: int(query.recover)], - "columnUses": [_NULL_FILTER_USE][: int(query.null_filter)], - } - dialect: str | None = query.profile.sql_analysis_dialect - try: - types, nullability, direct, parsed = _polyglot_cte_passthrough_facts( - polyglot_module=import_polyglot(), - cleaned_sql=query.sql, - dialect=dialect, - column_types_by_table=_SCHEMAS, - column_nullability_by_table=_INPUT_NULLABILITY, - inference_profile=query.profile, - analysis=analysis, - resolvers=_RESOLVERS, - ) - non_null: frozenset[str] = _polyglot_filtered_non_null_outputs( - polyglot_module=import_polyglot(), - cleaned_sql=query.sql, - dialect=dialect, - analysis=analysis, - column_nullability_by_table=_INPUT_NULLABILITY, - parsed=parsed, - ) - except Exception as error: # noqa: BLE001 - native must not answer where Python raises - return (type(error).__name__, str(error)) - return ( - list(types.items()), - [(name, value.value) for name, value in nullability.items()], - sorted(direct), - sorted(non_null), - ) - - def native_cte_facts(*, query: CteFactQuery) -> tuple[str | None, CteFactView]: """Native recovery's deferral reason, or None and its facts.""" @@ -424,11 +366,10 @@ def native_cte_facts(*, query: CteFactQuery) -> tuple[str | None, CteFactView]: def compare_cte_facts(*, query: CteFactQuery, parity: CteFactParity) -> None: - """Record both recoveries of `query` where native answers, and what each recovered.""" + """Record native recovery of `query` where it answers, and what it recovered.""" deferral, native = native_cte_facts(query=query) answered: list[CteFactView] = [native][: int(deferral is None)] - python: list[CteFactView] = [python_cte_facts(query=query) for _ in answered] parity.counts.update( [f"deferred:{str(deferral).partition(' ')[0]}"][: int(deferral is not None)] ) @@ -436,5 +377,4 @@ def compare_cte_facts(*, query: CteFactQuery, parity: CteFactParity) -> None: for index, kind in enumerate(_FACT_KINDS): parity.counts[f"recovered_{kind}"] += sum(bool(view[index]) for view in answered) parity.queries.extend((query.sql, query.profile.sql_analysis_dialect) for _ in answered) - parity.python.extend(python) parity.native.extend(answered) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index fe5228eae7..8afd00268b 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -110,3 +110,30 @@ class SharedQueryOutputTestCase: models: dict[str, str] expected_lineage: tuple[tuple[str, str, str], ...] = () expected_findings: tuple[tuple[str, str], ...] = () + + +@dataclass(frozen=True) +class FallbackColumnsTestCase: + """A model native analysis cannot project, and the columns its parsed tree still gives.""" + + description: str + query_sql: str + expected_columns: tuple[tuple[str, str | None, str], ...] | None + + +@dataclass(frozen=True) +class TableFunctionColumnsTestCase: + """A table function's declared return columns against its body's output columns.""" + + description: str + returns: str + body_sql: str + expected_error: str | None + + +@dataclass(frozen=True) +class MissingSessionAnalysisTestCase: + """A broken invariant: the session returns no analysis for a model it was asked about.""" + + description: str + expected_message: str diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index efa50f07a8..d7ec7a7e89 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -665,20 +665,6 @@ def recorded_model(*arguments: Any, **keywords: Any) -> CompiledModel: ) -def record_python_model_analyses(*, monkeypatch: pytest.MonkeyPatch) -> list[object]: - """Record every Python model analysis assembly runs; return the list they are appended to.""" - - calls: list[object] = [] - original: Callable[..., Any] = project_assembly.analyze_columns_and_lineage_with_polyglot - - def recorded(**keywords: Any) -> Any: - calls.append(keywords.get("query_sql")) - return original(**keywords) - - monkeypatch.setattr(project_assembly, "analyze_columns_and_lineage_with_polyglot", recorded) - return calls - - def duplicate_analysed_model_names(*, monkeypatch: pytest.MonkeyPatch) -> None: """Name every analysed model after the first, an invariant discovery guarantees (D007).""" @@ -713,7 +699,10 @@ def captured(request: NativeModelAnalysisRequest) -> tuple[dict[str, ModelSqlAna requests.append(request) return {}, None - with monkeypatch.context() as patch, suppress(CompileInputError): + with ( + monkeypatch.context() as patch, + suppress(CompileInputError, native_module.NativeCompilerError), + ): patch.setattr(project_assembly, "analyze_model_sql", captured) _ = project_assembly.assemble_compiled_project( inputs=inputs, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py index 119e8e0c71..f1e03acc70 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py @@ -1,4 +1,4 @@ -"""Native CTE fact recovery equals Python's recovery on seeded queries, query by query.""" +"""Native CTE fact recovery matches the facts recorded from Python's recovery, query by query.""" from __future__ import annotations @@ -15,7 +15,12 @@ from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( CteFactRecoveryParityTestCase, ) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.golden_views import ( + golden_differences, + golden_entry, + golden_name, + read_golden, +) @pytest.mark.parametrize( @@ -36,15 +41,21 @@ ], ids=lambda case: case.description, ) -def test_given_seeded_cte_queries_when_recovering_facts_natively_then_matches_python( +def test_given_seeded_cte_queries_when_recovering_facts_natively_then_matches_recorded_python( test_case: CteFactRecoveryParityTestCase, ) -> None: rng: random.Random = random.Random(test_case.seed) - parity: CteFactParity = CteFactParity(queries=[], python=[], native=[], counts=Counter()) + parity: CteFactParity = CteFactParity(queries=[], native=[], counts=Counter()) for _ in range(test_case.count): compare_cte_facts(query=generated_cte_fact_query(rng=rng), parity=parity) - assert mismatches(inputs=parity.queries, expected=parity.python, actual=parity.native) == [] + assert ( + golden_differences( + read_golden(golden_name("cte_facts", test_case.description)), + [golden_entry("cte_facts", view) for view in parity.native], + ) + == [] + ) assert parity.counts["compared"] >= test_case.expected_minimum_compared assert { kind: parity.counts[f"recovered_{kind}"] >= minimum diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index c05d76eee1..fb278d29bc 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -50,7 +50,6 @@ generated_analysis_files, native_pivot_proofs, pivot_project_files, - record_python_model_analyses, shared_analysis_files, started_sessions, ) @@ -425,7 +424,6 @@ def test_given_internal_native_failure_when_assembling_then_raises_without_pytho inputs: CompileProjectInputs = compile_inputs( project_dir=tmp_path / "project", files=test_case.files ) - python_analyses: list[object] = record_python_model_analyses(monkeypatch=monkeypatch) duplicate_analysed_model_names(monkeypatch=monkeypatch) with pytest.raises(native_module.NativeCompilerError) as raised: @@ -434,9 +432,8 @@ def test_given_internal_native_failure_when_assembling_then_raises_without_pytho inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb"), ) - assert (str(raised.value), python_analyses, deferral_kinds(record_dir)) == ( + assert (str(raised.value), deferral_kinds(record_dir)) == ( test_case.expected_message, - [], Counter(), ) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py new file mode 100644 index 0000000000..379392f8df --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py @@ -0,0 +1,142 @@ +"""Column inference outside the analysis session: models native analysis cannot project and +table-function bodies keep the columns Python's parsed-tree inference gave them. + +Expected outputs recorded from the pre-port Python `infer_columns_with_sql_analysis`. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +import sqlbuild._native as native_module +import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly +from sqlbuild.compiler.compile.exceptions import CompileInputError +from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject +from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + FallbackColumnsTestCase, + MissingSessionAnalysisTestCase, + TableFunctionColumnsTestCase, +) +from tests.integration.src.sqlbuild.compiler.lineage.helpers import compiled_project + +_PROJECT: str = 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n' +_HEADER: str = "MODEL (description 'Test model.', materialized view);\n" + + +@pytest.mark.parametrize( + "test_case", + [ + FallbackColumnsTestCase( + description="CREATE TABLE AS takes its inner select's columns", + query_sql="CREATE TABLE t AS SELECT 1 AS a", + expected_columns=(("a", None, "non_null"),), + ), + FallbackColumnsTestCase( + description="INSERT ... SELECT takes its select's columns", + query_sql="INSERT INTO t SELECT 1 AS a", + expected_columns=(("a", None, "non_null"),), + ), + FallbackColumnsTestCase( + description="calls nested 200 deep", + query_sql="SELECT " + "f(" * 200 + "1" + ")" * 200 + " AS deep", + expected_columns=(("deep", None, "unknown"),), + ), + ], + ids=lambda case: case.description, +) +def test_given_unprojectable_model_when_compiling_then_parsed_tree_columns_are_kept( + test_case: FallbackColumnsTestCase, tmp_path: Path +) -> None: + project: CompiledProject = compiled_project( + project_dir=tmp_path, + files={ + "sqlbuild_project.toml": _PROJECT, + "models/orders.sql": _HEADER + test_case.query_sql, + }, + ) + model: CompiledModel = project.models[0] + + assert ( + None + if model.inferred_columns is None + else tuple( + (column.name, column.type, column.nullability.value) + for column in model.inferred_columns + ) + ) == test_case.expected_columns + + +@pytest.mark.parametrize( + "test_case", + [ + TableFunctionColumnsTestCase( + description="matching return columns", + returns="table (id INTEGER, label VARCHAR)", + body_sql="SELECT x AS id, 'a' AS label", + expected_error=None, + ), + TableFunctionColumnsTestCase( + description="fewer body columns than declared", + returns="table (id INTEGER, label VARCHAR)", + body_sql="SELECT x AS id", + expected_error=( + "SQL table function functions/sql/table_fn__orders.sql declares 2 return " + "columns but its query produces 1" + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_table_function_when_compiling_then_body_columns_match_declaration( + test_case: TableFunctionColumnsTestCase, tmp_path: Path +) -> None: + files: dict[str, str] = { + "sqlbuild_project.toml": _PROJECT, + "functions/sql/table_fn__orders.sql": ( + "FUNCTION (description 'Test function.', arguments (x INTEGER), " + f"returns {test_case.returns}); {test_case.body_sql}" + ), + } + error: str | None = None + try: + _ = compiled_project(project_dir=tmp_path, files=files) + except CompileInputError as raised: + error = str(raised) + + assert error == test_case.expected_error + + +@pytest.mark.parametrize( + "test_case", + [ + MissingSessionAnalysisTestCase( + description="a requested model the session left unanalysed", + expected_message=( + "NativeCompilerError: native model analysis: the session returned no analysis " + "for model 'orders'" + ), + ) + ], + ids=lambda case: case.description, +) +def test_given_session_without_model_analysis_when_assembling_then_raises_native_compiler_error( + test_case: MissingSessionAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr(project_assembly, "analyze_model_sql", lambda _request: ({}, None)) + + with pytest.raises(native_module.NativeCompilerError) as raised: + _ = compiled_project( + project_dir=tmp_path, + files={ + "sqlbuild_project.toml": _PROJECT, + "models/orders.sql": _HEADER + "SELECT 1 AS a", + }, + ) + + assert str(raised.value) == test_case.expected_message + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py index 81bd7146b1..3d75e75b04 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py @@ -3,13 +3,10 @@ import json from collections.abc import Callable from pathlib import Path -from types import ModuleType from typing import Any from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter -from sqlbuild.compiler.compile._helpers.analysis import compact -from sqlbuild.compiler.compile._helpers.sharing import binding as binding_sharing from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( CompileAdapterContext, @@ -24,12 +21,10 @@ from sqlbuild.compiler.pipeline.main.compile import run_compile_pipeline from sqlbuild.compiler.pipeline.main.project import compile_project from sqlbuild.compiler.pipeline.models import CompilePipelineOptions, CompilePipelineResult -from sqlbuild.compiler.sql_analysis.main import _normalize_analysis_batch as normalize_batch from sqlbuild.runtime.contracts.models import ConnectionHooks from sqlbuild.sql_values.types import CollectionRendering _REPOSITORY_ROOT: Path = Path(__file__).resolve().parents[6] -_NORMALIZATION_MODULES: tuple[ModuleType, ...] = (normalize_batch, binding_sharing, compact) _ANALYSIS_CACHE_METRICS: tuple[str, ...] = ( "analysis_batch_cache_hits", "analysis_entry_cache_hits", diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/_test_types.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/_test_types.py index 0a910a212c..7f2d2cefb1 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/_test_types.py @@ -16,14 +16,6 @@ class DynamicPivotAnalysisTestCase: aggregate: str = "MAX" -@dataclass(frozen=True) -class CompactAnalysisWorkersTestCase: - description: str - model_count: int - sql_bytes_per_model: int - expected_workers: int - - @dataclass(frozen=True) class SetOperationSearchTestCase: description: str diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_compact_workers.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_compact_workers.py deleted file mode 100644 index 2c70492d97..0000000000 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/analysis/test_compact_workers.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -import orjson -import pytest - -from sqlbuild.compiler.compile._helpers.analysis import compact -from sqlbuild.compiler.compile.models import CompactBatchPreparation -from tests.unit.src.sqlbuild.compiler.compile._helpers.analysis._test_types import ( - CompactAnalysisWorkersTestCase, -) - -_MIB: int = 1024 * 1024 - - -@pytest.mark.parametrize( - "test_case", - ( - CompactAnalysisWorkersTestCase( - description="small project", - model_count=3, - sql_bytes_per_model=64, - expected_workers=4, - ), - CompactAnalysisWorkersTestCase( - description="dense ten thousand model project", - model_count=10_000, - sql_bytes_per_model=(64 * _MIB) // 10_000, - expected_workers=4, - ), - ), - ids=lambda case: case.description, -) -def test_given_project_size_when_running_compact_analysis_then_native_uses_all_workers( - test_case: CompactAnalysisWorkersTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - captured: list[dict[str, object]] = [] - - def analyze(request_json: str) -> str: - captured.append(orjson.loads(request_json)) - return "{}" - - monkeypatch.setattr(compact._native, "analyze_project_queries_compact_json", analyze) - sql: str = "SELECT 1 AS order_id" + " " * (test_case.sql_bytes_per_model - 20) - preparation: CompactBatchPreparation = CompactBatchPreparation( - cleaned_sql=(sql,) * test_case.model_count, - queries=({"sql": "SELECT 1 AS order_id", "dialect": "duckdb"},), - templates=({"queryIndex": 0},), - projections=({"templateIndex": 0},) * test_case.model_count, - ) - - _ = compact._run_compact_analysis_batch(preparation=preparation) - - assert [request["workers"] for request in captured] == [test_case.expected_workers] - - -if __name__ == "__main__": - raise SystemExit(pytest.main([__file__, "-n", "auto", "--dist", "loadfile"])) diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/render/test_cursor_intrinsics.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/render/test_cursor_intrinsics.py index f053717e22..e8956a2043 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/render/test_cursor_intrinsics.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/render/test_cursor_intrinsics.py @@ -2,7 +2,11 @@ import pytest -from sqlbuild.compiler.compile._helpers.analysis.compact import infer_columns_with_sql_analysis +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, +) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery from sqlbuild.compiler.compile._helpers.render.cursor_intrinsics import ( cursor_intrinsics_analysis_sql, get_validated_model_cursor_intrinsics, @@ -154,7 +158,10 @@ def test_given_typed_intrinsic_when_analyzing_then_infers_non_null_column( cursor_type=test_case.cursor_type, ) - assert infer_columns_with_sql_analysis(query_sql=analysis_sql) == ( + assert infer_native_query_columns( + queries=(NativeColumnQuery(sql=analysis_sql, mode="parse"),), + profile=ExpressionInferenceProfile(), + )[0].columns == ( InferredColumn( name="batch_start", type=test_case.expected_type, diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py index 93fa1ead40..e00468800a 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py @@ -1,30 +1,20 @@ from __future__ import annotations -import json -from typing import cast -from unittest.mock import Mock - import pytest -from polyglot_sql import ParseError from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.analysis import columns as analysis_columns -from sqlbuild.compiler.compile._helpers.analysis.columns import ( - import_polyglot_sql, - substitute_placeholder_defaults, +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, ) -from sqlbuild.compiler.compile._helpers.analysis.compact import ( - analyze_columns_and_lineage_with_polyglot, - analyze_queries_with_compact_polyglot_batch, - infer_columns_with_sql_analysis, +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns +from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( + substitute_placeholder_defaults, ) from sqlbuild.compiler.compile.models import ( CompiledLineageColumnFact, CompiledLineageSourceFact, CompileSqlReference, InferredColumn, - NativeCompactAnalysis, - PolyglotAnalysisResult, ) from sqlbuild.compiler.compile.types import CompiledResourceType from sqlbuild.compiler.lineage.types import ( @@ -34,501 +24,37 @@ ) from sqlbuild.compiler.references.types import SqlReferenceKind from tests.unit.src.sqlbuild.compiler.compile._helpers._test_types import ( - ExpectedCountTestCase, InferColumnsTestCase, - NativeTypeInferenceModeTestCase, PolyglotAnalysisTestCase, - QualifiedReferenceAnalysisTestCase, - QualifiedReferenceScanTestCase, SubstitutePlaceholderDefaultsTestCase, - UnexpectedAnalysisFailureTestCase, ) from tests.unit.src.sqlbuild.compiler.compile._helpers.helpers import direct_orders_lineage -@pytest.mark.parametrize( - "test_case", - [ExpectedCountTestCase(description="native request remains batched", expected_count=1)], - ids=lambda case: case.description, -) -def test_given_queries_when_batch_analyzing_then_uses_one_ordered_native_request( - monkeypatch: pytest.MonkeyPatch, - test_case: ExpectedCountTestCase, -) -> None: - captured: list[dict[str, object]] = [] - - def analyze_project_queries_compact_json(request_json: str) -> str: - request: dict[str, object] = json.loads(request_json) - captured.append(request) - return json.dumps( - { - "strings": [ - "order_id", - "BIGINT", - "model", - "__sqlbuild_project_input_0", - "orders", - ], - "facts": [[0, 1, 1, 0, 1, [[2, 3, 0]]]], - "templates": [ - [[0], False], - "invalid query", - ], - "analyses": [ - [0, [[3, 4]]], - [1, []], - ], - } - ) - - monkeypatch.setattr( - "sqlbuild.compiler.compile._helpers.analysis.compact._native.analyze_project_queries_compact_json", - analyze_project_queries_compact_json, - raising=False, - ) - - results: tuple[NativeCompactAnalysis, ...] = analyze_queries_with_compact_polyglot_batch( - query_sqls=( - 'SELECT order_id FROM __ref("orders")', - "SELECT @@@status AS status", - ), - references=( - (CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="orders"),), - (), - ), - placeholders=(None, {"status": "'ready'"}), - column_nullability_by_table={"orders": {"order_id": InferredNullability.NON_NULL}}, - column_types_by_table={"orders": {"order_id": "BIGINT"}}, - inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb"), - recover_cte_facts=(False, False), - ) - - assert len(captured) == test_case.expected_count - native_queries: list[dict[str, object]] = cast(list[dict[str, object]], captured[0]["queries"]) - native_templates: list[dict[str, object]] = cast( - list[dict[str, object]], captured[0]["templates"] - ) - native_projections: list[dict[str, object]] = cast( - list[dict[str, object]], captured[0]["projections"] - ) - assert len(native_queries) == 2 - assert "__ref" not in str(native_queries[0]["sql"]) - assert "__sqlbuild_project_input_0" in str(native_queries[0]["sql"]) - assert native_queries[1]["sql"] == "SELECT 'ready' AS status" - assert native_templates[0]["references"] == { - "__sqlbuild_project_input_0": { - "resourceType": "model", - "resourceName": "__sqlbuild_project_input_0", - } - } - assert native_projections[0]["resourceNames"] == {"__sqlbuild_project_input_0": "orders"} - assert native_queries[0]["schema"] == { - "tables": [ - { - "name": "__sqlbuild_project_input_0", - "columns": [{"name": "order_id", "type": "BIGINT", "nullable": False}], - } - ] - } - - assert "orders" in results[0].cleaned_sql - assert results[0].analysis == {"hasStar": False, "starResolved": False} - assert results[0].compact_rows == [0] - assert results[1].analysis is None - assert results[1].projected is True - projected_analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql='SELECT order_id FROM __ref("orders")', - allow_compact_analysis=True, - precomputed=results[0], - ) - assert projected_analysis.columns == ( - InferredColumn( - name="order_id", - type="BIGINT", - nullability=InferredNullability.NON_NULL, - ), - ) - assert tuple(projected_analysis.lineage_columns) == ( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ) - monkeypatch.setattr( - "sqlbuild.compiler.compile._helpers.analysis.compact.import_polyglot_sql", - Mock(side_effect=AssertionError("native failures must not trigger Python reparsing")), - ) - - failed_analysis: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql="SELECT FROM", - allow_compact_analysis=True, - precomputed=results[1], - ) - - assert failed_analysis.analysis_succeeded is False - - -@pytest.mark.parametrize( - "test_case", - ( - InferColumnsTestCase( - description="cast expression overrides input type through CTE", - query_sql='WITH typed AS (SELECT COALESCE(CAST(amount AS FLOAT), CAST(0 AS FLOAT)) AS amount FROM __ref("orders")) SELECT amount FROM typed', - expected_columns=( - InferredColumn( - name="amount", type="FLOAT", nullability=InferredNullability.NON_NULL - ), - ), - ), - ), - ids=lambda case: case.description, -) -def test_given_cte_cast_when_batch_analyzing_then_cast_type_overrides_input_type( - test_case: InferColumnsTestCase, -) -> None: - query_sql: str = test_case.query_sql - references: tuple[CompileSqlReference, ...] = ( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="orders"), - ) - nullability: dict[str, dict[str, InferredNullability]] = { - "orders": {"amount": InferredNullability.NULLABLE} - } - types: dict[str, dict[str, str]] = {"orders": {"amount": "VARCHAR"}} - profile: ExpressionInferenceProfile = ExpressionInferenceProfile( - sql_analysis_dialect="snowflake" - ) - - prepared: NativeCompactAnalysis = analyze_queries_with_compact_polyglot_batch( - query_sqls=(query_sql,), - references=(references,), - placeholders=(None,), - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - recover_cte_facts=(True,), - rich_type_inference=False, - )[0] - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=query_sql, - references=references, - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - allow_compact_analysis=True, - recover_cte_facts=True, - precomputed=prepared, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - - -@pytest.mark.parametrize( - "test_case", - ( - InferColumnsTestCase( - description="lowercase authored stub-named CTE", - query_sql=( - "WITH __sqlbuild_project_input_0 AS (SELECT 'pending' AS order_id) " - 'SELECT order_id FROM __ref("orders")' - ), - expected_columns=( - InferredColumn( - name="order_id", type="BIGINT", nullability=InferredNullability.NON_NULL - ), - ), - ), - InferColumnsTestCase( - description="uppercase authored stub-named CTE", - query_sql=( - "WITH __SQLBUILD_PROJECT_INPUT_0 AS (SELECT 'pending' AS order_id) " - 'SELECT order_id FROM __ref("orders")' - ), - expected_columns=( - InferredColumn( - name="order_id", type="BIGINT", nullability=InferredNullability.NON_NULL - ), - ), - ), - ), - ids=lambda case: case.description, -) -def test_given_authored_stub_named_cte_when_batch_analyzing_without_binding_then_uses_input( - test_case: InferColumnsTestCase, -) -> None: - references: tuple[CompileSqlReference, ...] = ( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="orders"), - ) - nullability: dict[str, dict[str, InferredNullability]] = { - "orders": {"order_id": InferredNullability.NON_NULL} - } - types: dict[str, dict[str, str]] = {"orders": {"order_id": "BIGINT"}} - profile: ExpressionInferenceProfile = ExpressionInferenceProfile(sql_analysis_dialect="duckdb") - - prepared: NativeCompactAnalysis = analyze_queries_with_compact_polyglot_batch( - query_sqls=(test_case.query_sql,), - references=(references,), - placeholders=(None,), - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - recover_cte_facts=(False,), - )[0] - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=references, - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - allow_compact_analysis=True, - precomputed=prepared, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - lineage_column: CompiledLineageColumnFact = tuple(result.lineage_columns)[0] - assert [source.resource_name for source in lineage_column.upstream_columns] == ["orders"] - - -@pytest.mark.parametrize( - "test_case", - [ - QualifiedReferenceAnalysisTestCase( - description="plain qualified reference", - query_sql='SELECT orders.order_id FROM __ref("orders")', - ), - QualifiedReferenceAnalysisTestCase( - description="commented qualified reference", - query_sql='SELECT orders /* column qualifier */ .order_id FROM __ref("orders")', - ), - ], - ids=lambda case: case.description, -) -def test_given_qualified_reference_when_batch_analyzing_then_preserves_analysis_facts( - test_case: QualifiedReferenceAnalysisTestCase, -) -> None: - references: tuple[CompileSqlReference, ...] = ( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="orders"), - ) - nullability: dict[str, dict[str, InferredNullability]] = { - "orders": {"order_id": InferredNullability.NON_NULL} - } - types: dict[str, dict[str, str]] = {"orders": {"order_id": "BIGINT"}} - profile: ExpressionInferenceProfile = ExpressionInferenceProfile(sql_analysis_dialect="duckdb") - expected: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=references, - placeholders=None, - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - allow_compact_analysis=True, - ) - prepared: NativeCompactAnalysis = analyze_queries_with_compact_polyglot_batch( - query_sqls=(test_case.query_sql,), - references=(references,), - placeholders=(None,), - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - recover_cte_facts=(False,), +def _reanalysed( + *, + query_sql: str, + references: tuple[CompileSqlReference, ...] = (), + column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, + column_types_by_table: dict[str, dict[str, str]] | None = None, + inference_profile: ExpressionInferenceProfile | None = None, + allow_compact_analysis: bool = True, + recover_cte_facts: bool = True, +) -> NativeQueryColumns: + """Native compact re-analysis over known input types, as source bindings run it.""" + + assert allow_compact_analysis and recover_cte_facts + assert all( + value == InferredNullability.UNKNOWN + for shape in (column_nullability_by_table or {}).values() + for value in shape.values() + ) + return infer_native_query_columns( + queries=(NativeColumnQuery(sql=query_sql, mode="reanalysis", references=references),), + profile=inference_profile or ExpressionInferenceProfile(), + column_types_by_table=column_types_by_table, )[0] - actual: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=references, - placeholders=None, - column_nullability_by_table=nullability, - column_types_by_table=types, - inference_profile=profile, - allow_compact_analysis=True, - precomputed=prepared, - ) - - assert (actual.columns == expected.columns) is test_case.expected_matches - assert tuple(actual.lineage_columns) == expected.lineage_columns - - -@pytest.mark.parametrize( - "test_case", - (ExpectedCountTestCase(description="unqualified SQL skips scanner", expected_count=0),), - ids=lambda case: case.description, -) -def test_given_sql_without_dots_when_finding_qualified_references_then_skips_scanner( - monkeypatch: pytest.MonkeyPatch, - test_case: ExpectedCountTestCase, -) -> None: - scanner: Mock = Mock() - monkeypatch.setattr(analysis_columns, "_QUALIFIED_IDENTIFIER_PATTERN", scanner) - - result: frozenset[str] = analysis_columns._qualified_reference_names( - query_sql='SELECT order_id FROM __ref("orders")', - reference_names=("orders",), - ) - - assert result == frozenset() - assert scanner.finditer.call_count == test_case.expected_count - - -@pytest.mark.parametrize( - "test_case", - ( - QualifiedReferenceScanTestCase( - description="alias qualifier does not qualify the relation", - query_sql="SELECT input.id FROM orders AS input", - reference_names=("orders",), - expected_names=frozenset(), - ), - QualifiedReferenceScanTestCase( - description="relation qualifier matches case-insensitively", - query_sql="SELECT ORDERS . id FROM orders", - reference_names=("orders", "Orders"), - expected_names=frozenset({"orders", "Orders"}), - ), - QualifiedReferenceScanTestCase( - description="longer identifier sharing a prefix is not a qualifier", - query_sql="SELECT orders_archive.id, x.orders FROM orders_archive, orders AS x", - reference_names=("orders", "archive"), - expected_names=frozenset(), - ), - QualifiedReferenceScanTestCase( - description="dollar and digit identifier characters bound the name", - query_sql="SELECT $orders.id, orders$1.id, orders.id FROM t", - reference_names=("orders",), - expected_names=frozenset({"orders"}), - ), - QualifiedReferenceScanTestCase( - description="string literal text still counts like the regex scan", - query_sql="SELECT 'customers.id' AS note FROM orders", - reference_names=("orders", "customers"), - expected_names=frozenset({"customers"}), - ), - QualifiedReferenceScanTestCase( - description="comment between qualifier and separator", - query_sql="SELECT orders /* note */ .id -- customers.id\nFROM orders", - reference_names=("orders", "customers"), - expected_names=frozenset({"orders", "customers"}), - ), - QualifiedReferenceScanTestCase( - description="comment consumed by an earlier qualifier hides its contents", - query_sql="SELECT x /* customers. */ .id FROM orders AS x", - reference_names=("orders", "customers"), - expected_names=frozenset(), - ), - QualifiedReferenceScanTestCase( - description="quoted identifiers qualify by their unquoted name", - query_sql='SELECT "orders".id, `customers`.id, [products].id FROM t', - reference_names=("orders", "customers", "products"), - expected_names=frozenset({"orders", "customers", "products"}), - ), - QualifiedReferenceScanTestCase( - description="quoted identifier containing a dot is one name", - query_sql='SELECT "orders.v2".id FROM t', - reference_names=("orders",), - expected_names=frozenset(), - ), - QualifiedReferenceScanTestCase( - description="non-identifier reference names never qualify", - query_sql="SELECT order-lines.id FROM t", - reference_names=("order-lines", "lines"), - expected_names=frozenset({"lines"}), - ), - QualifiedReferenceScanTestCase( - description="non-ascii SQL uses the full scan", - query_sql="SELECT caf\u00e9.id, orders.id FROM t", - reference_names=("orders", "caf\u00e9"), - expected_names=frozenset({"orders"}), - ), - ), - ids=lambda case: case.description, -) -def test_given_reference_names_when_finding_qualified_references_then_matches_full_scan( - test_case: QualifiedReferenceScanTestCase, -) -> None: - names_by_normalized: dict[str, list[str]] = {} - for name in test_case.reference_names: - names_by_normalized.setdefault(name.casefold(), []).append(name) - - result: frozenset[str] = analysis_columns._qualified_reference_names( - query_sql=test_case.query_sql, reference_names=test_case.reference_names - ) - - assert result == test_case.expected_names - assert ( - analysis_columns._scanned_qualified_reference_names( - query_sql=test_case.query_sql, names_by_normalized=names_by_normalized - ) - == test_case.expected_names - ) - - -@pytest.mark.parametrize( - "test_case", - [ - NativeTypeInferenceModeTestCase( - description="direct input type remains unknown in fast mode", - query_sql='SELECT status FROM __ref("orders")', - column_types={"status": "VARCHAR"}, - expected_fast_types=(None,), - expected_rich_types=("TEXT",), - ), - NativeTypeInferenceModeTestCase( - description="aggregate hint remains unknown in fast mode", - query_sql='SELECT SUM(amount) AS total FROM __ref("orders")', - column_types={"amount": "INT"}, - expected_fast_types=(None,), - expected_rich_types=("INT128",), - ), - ], - ids=lambda case: case.description, -) -def test_given_native_batch_when_selecting_type_mode_then_preserves_mode_contract( - test_case: NativeTypeInferenceModeTestCase, -) -> None: - references: tuple[CompileSqlReference, ...] = ( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="orders"), - ) - nullability: dict[str, dict[str, InferredNullability]] = { - "orders": {name: InferredNullability.NON_NULL for name in test_case.column_types} - } - profile: ExpressionInferenceProfile = ExpressionInferenceProfile(sql_analysis_dialect="duckdb") - - projected_types: list[tuple[str | None, ...]] = [] - for rich_type_inference in (False, True): - prepared: NativeCompactAnalysis = analyze_queries_with_compact_polyglot_batch( - query_sqls=(test_case.query_sql,), - references=(references,), - placeholders=(None,), - column_nullability_by_table=nullability, - column_types_by_table={"orders": test_case.column_types}, - inference_profile=profile, - recover_cte_facts=(False,), - rich_type_inference=rich_type_inference, - )[0] - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=references, - column_nullability_by_table=nullability, - column_types_by_table={"orders": test_case.column_types}, - inference_profile=profile, - allow_compact_analysis=True, - precomputed=prepared, - ) - projected_types.append(tuple(column.type for column in result.columns or ())) - - assert tuple(projected_types) == ( - test_case.expected_fast_types, - test_case.expected_rich_types, - ) - @pytest.mark.parametrize( "test_case", @@ -999,11 +525,11 @@ def test_given_native_batch_when_selecting_type_mode_then_preserves_mode_contrac def test_given_query_sql_when_inferring_columns_then_returns_expected( test_case: InferColumnsTestCase, ) -> None: - result: tuple[InferredColumn, ...] | None = infer_columns_with_sql_analysis( - query_sql=test_case.query_sql, + result: tuple[InferredColumn, ...] | None = infer_native_query_columns( + queries=(NativeColumnQuery(sql=test_case.query_sql, mode="parse"),), + profile=test_case.inference_profile or ExpressionInferenceProfile(), column_nullability_by_table=test_case.column_nullability_by_table, - inference_profile=test_case.inference_profile, - ) + )[0].columns assert result == test_case.expected_columns @@ -1032,65 +558,6 @@ def test_given_query_sql_when_inferring_columns_then_returns_expected( ), expected_has_star=False, ), - PolyglotAnalysisTestCase( - description="refines a compact filtered column across whitespace", - query_sql=('SELECT order_id FROM __ref("orders") WHERE order_id IS\nNOT NULL'), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={"orders": {"order_id": InferredNullability.NULLABLE}}, - column_types_by_table={"orders": {"order_id": "INTEGER"}}, - expected_columns=( - InferredColumn( - name="order_id", - type="INT", - nullability=InferredNullability.NON_NULL, - ), - ), - expected_lineage_columns=direct_orders_lineage("order_id"), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="refines a compact filtered outer join across a comment", - query_sql=( - 'SELECT c.customer_id FROM __ref("orders") AS o ' - 'LEFT JOIN __ref("customers") AS c ' - "ON o.order_id = c.customer_id " - "WHERE c.customer_id IS /* required customer */ NOT NULL" - ), - references=( - CompileSqlReference(SqlReferenceKind.REF, "orders"), - CompileSqlReference(SqlReferenceKind.REF, "customers"), - ), - column_nullability_by_table={ - "orders": {"order_id": InferredNullability.NON_NULL}, - "customers": {"customer_id": InferredNullability.NON_NULL}, - }, - column_types_by_table={ - "orders": {"order_id": "INTEGER"}, - "customers": {"customer_id": "INTEGER"}, - }, - expected_columns=( - InferredColumn( - name="customer_id", - type="INT", - nullability=InferredNullability.NON_NULL, - ), - ), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="customer_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="customers", - column_name="customer_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), PolyglotAnalysisTestCase( description="terminates direct lineage at a managed table function output", query_sql='SELECT order_id FROM __table_fn("customer_orders")(42)', @@ -1144,7 +611,7 @@ def test_given_query_sql_when_inferring_columns_then_returns_expected( def test_given_ref_query_when_analyzing_columns_and_lineage_then_returns_compact_facts( test_case: PolyglotAnalysisTestCase, ) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( + result: NativeQueryColumns = _reanalysed( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, @@ -1154,9 +621,8 @@ def test_given_ref_query_when_analyzing_columns_and_lineage_then_returns_compact recover_cte_facts=True, ) - assert result.analysis_succeeded + assert result.succeeded assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns assert result.has_star is test_case.expected_has_star @@ -1164,373 +630,23 @@ def test_given_ref_query_when_analyzing_columns_and_lineage_then_returns_compact "test_case", [ PolyglotAnalysisTestCase( - description="preserves fallback lineage for a commented qualified column", + description="does not reuse a source type through a transformed cte", query_sql=( - "SELECT orders.order_id,\n" - "-- Current order status.\n" - 'orders.status FROM __ref("orders") AS orders' + "WITH transformed AS (" + "SELECT amount || '0' AS amount FROM __ref(\"orders\")" + "), final AS (" + "SELECT amount FROM transformed" + ") SELECT amount FROM final" ), references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(InferredColumn(name="order_id"), InferredColumn(name="status")), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - CompiledLineageColumnFact( - output_column="status", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="status", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), + column_nullability_by_table={"orders": {"amount": InferredNullability.UNKNOWN}}, + column_types_by_table={"orders": {"amount": "INTEGER"}}, + expected_columns=(InferredColumn(name="amount"),), + expected_lineage_columns=direct_orders_lineage("amount"), expected_has_star=False, ), PolyglotAnalysisTestCase( - description="preserves a final CTE cast after a values CTE on the AST fallback", - query_sql=( - "WITH labels(label) AS (VALUES ('web')), transformed AS (" - 'SELECT CAST(amount AS INTEGER) AS amount FROM __ref("orders")' - "), final AS (SELECT amount FROM transformed) SELECT amount FROM final" - ), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={"orders": {"amount": InferredNullability.UNKNOWN}}, - column_types_by_table={"orders": {"amount": "VARCHAR"}}, - expected_columns=(InferredColumn(name="amount", type="INT"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="amount", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="amount", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.MEDIUM, - ), - ), - expected_has_star=False, - ), - ], - ids=lambda case: case.description, -) -def test_given_annotated_projection_when_using_ast_fallback_then_lineage_is_preserved( - test_case: PolyglotAnalysisTestCase, -) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - column_nullability_by_table=test_case.column_nullability_by_table, - column_types_by_table=test_case.column_types_by_table, - allow_compact_analysis=False, - recover_cte_facts=True, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns - assert result.has_star is test_case.expected_has_star - - -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="uses compact query analysis before AST fallback", - query_sql='SELECT order_id FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(InferredColumn(name="order_id"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ) - ], - ids=lambda case: case.description, -) -def test_given_compact_query_analysis_when_ast_parse_would_fail_then_returns_compact_facts( - test_case: PolyglotAnalysisTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - polyglot_module: object | None = import_polyglot_sql() - assert polyglot_module is not None - - def raise_parse_error(*args: object, **kwargs: object) -> object: - del args, kwargs - raise AssertionError("AST parse should not be called") - - monkeypatch.setattr(polyglot_module, "parse_one", raise_parse_error) - - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - allow_compact_analysis=True, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns - assert result.has_star is test_case.expected_has_star - - -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="uses compact analysis for aggregate transforms", - query_sql='SELECT COUNT(*) AS n, SUM(amount) AS total FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=( - InferredColumn(name="n", type="BIGINT", nullability=InferredNullability.NON_NULL), - InferredColumn(name="total", type="DECIMAL"), - ), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="n", - upstream_columns=(), - transform_kind=ColumnTransformKind.AGGREGATION, - confidence=ColumnLineageConfidence.UNKNOWN, - ), - CompiledLineageColumnFact( - output_column="total", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="amount", - ), - ), - transform_kind=ColumnTransformKind.AGGREGATION, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="uses compact analysis for expression transforms", - query_sql='SELECT amount + tax AS total FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(InferredColumn(name="total"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="total", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="amount", - ), - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="tax", - ), - ), - transform_kind=ColumnTransformKind.EXPRESSION, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="uses compact analysis for cte lineage", - query_sql=( - 'WITH base AS (SELECT order_id FROM __ref("orders")) SELECT order_id FROM base' - ), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(InferredColumn(name="order_id"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="uses compact analysis for set operation branch lineage", - query_sql=( - 'SELECT order_id FROM __ref("orders") UNION ALL SELECT return_id FROM __ref("returns")' - ), - references=( - CompileSqlReference(SqlReferenceKind.REF, "orders"), - CompileSqlReference(SqlReferenceKind.REF, "returns"), - ), - expected_columns=(InferredColumn(name="order_id"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="returns", - column_name="return_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="uses compact analysis for qualified stars", - query_sql=( - 'SELECT o.* FROM __ref("orders") o JOIN __ref("customers") c ON o.customer_id = c.id' - ), - references=( - CompileSqlReference(SqlReferenceKind.REF, "orders"), - CompileSqlReference(SqlReferenceKind.REF, "customers"), - ), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=True, - ), - PolyglotAnalysisTestCase( - description="uses compact schema metadata for unqualified column resolution", - query_sql=( - 'SELECT amount FROM __ref("orders") o JOIN __ref("customers") c ON o.customer_id = c.id' - ), - references=( - CompileSqlReference(SqlReferenceKind.REF, "orders"), - CompileSqlReference(SqlReferenceKind.REF, "customers"), - ), - column_nullability_by_table={ - "orders": { - "amount": InferredNullability.UNKNOWN, - "customer_id": InferredNullability.UNKNOWN, - }, - "customers": {"id": InferredNullability.UNKNOWN}, - }, - expected_columns=(InferredColumn(name="amount"),), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="amount", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="amount", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=False, - ), - ], - ids=lambda case: case.description, -) -def test_given_supported_compact_query_when_ast_parse_would_fail_then_returns_analysis_facts( - test_case: PolyglotAnalysisTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - polyglot_module: object | None = import_polyglot_sql() - assert polyglot_module is not None - - def raise_parse_error(*args: object, **kwargs: object) -> object: - del args, kwargs - raise AssertionError("AST parse should not be called") - - monkeypatch.setattr(polyglot_module, "parse_one", raise_parse_error) - - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - column_nullability_by_table=test_case.column_nullability_by_table, - allow_compact_analysis=True, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns - assert result.has_star is test_case.expected_has_star - - -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="preserves a cast type through direct cte passthroughs", - query_sql=( - "WITH transformed AS (" - 'SELECT CAST(amount AS INTEGER) AS amount FROM __ref("orders")' - "), final AS (" - "SELECT amount FROM transformed" - ") SELECT amount FROM final" - ), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={"orders": {"amount": InferredNullability.NON_NULL}}, - column_types_by_table={"orders": {"amount": "VARCHAR"}}, - expected_columns=( - InferredColumn( - name="amount", - type="INT", - nullability=InferredNullability.NON_NULL, - ), - ), - expected_lineage_columns=direct_orders_lineage("amount"), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="does not reuse a source type through a transformed cte", - query_sql=( - "WITH transformed AS (" - "SELECT amount || '0' AS amount FROM __ref(\"orders\")" - "), final AS (" - "SELECT amount FROM transformed" - ") SELECT amount FROM final" - ), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={"orders": {"amount": InferredNullability.UNKNOWN}}, - column_types_by_table={"orders": {"amount": "INTEGER"}}, - expected_columns=(InferredColumn(name="amount"),), - expected_lineage_columns=direct_orders_lineage("amount"), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="preserves only matching recursive by-name cte union types", + description="preserves only matching recursive by-name cte union types", query_sql=( "WITH current_orders AS (" "SELECT CAST(amount AS INTEGER) AS amount, " @@ -1709,7 +825,7 @@ def raise_parse_error(*args: object, **kwargs: object) -> object: def test_given_cte_chain_when_analyzing_direct_passthrough_then_type_is_conservative( test_case: PolyglotAnalysisTestCase, ) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( + result: NativeQueryColumns = _reanalysed( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, @@ -1719,193 +835,14 @@ def test_given_cte_chain_when_analyzing_direct_passthrough_then_type_is_conserva recover_cte_facts=True, ) - assert result.analysis_succeeded + assert result.succeeded assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns assert result.has_star is test_case.expected_has_star @pytest.mark.parametrize( "test_case", [ - PolyglotAnalysisTestCase( - description="preserves safe expression types without over-inferring overloads", - query_sql=( - "WITH transformed AS (" - "SELECT " - "'web' AS source_name, " - "TRUE AS active, " - "order_id IN (1, 2) AS selected, " - "first_name || last_name AS full_name, " - "COALESCE(status, CAST(NULL AS VARCHAR)) AS status, " - "IFF(active, status, NULL) AS maybe_status, " - "CASE WHEN active THEN status ELSE NULL END AS case_status, " - "NULLIF(status, '') AS clean_status, " - "MIN(created_at) OVER (PARTITION BY order_id) AS first_created_at, " - "to_date, " - "payload || payload AS merged_payload, " - "SUBSTRING(payload, 1, 2) AS sliced_payload " - 'FROM __ref("orders")' - "), final AS (" - "SELECT source_name, active, selected, full_name, status, maybe_status, " - "case_status, clean_status, first_created_at, to_date, merged_payload, " - "sliced_payload FROM transformed" - ") SELECT source_name, active, selected, full_name, status, maybe_status, " - "case_status, clean_status, first_created_at, to_date, merged_payload, " - "sliced_payload FROM final" - ), - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={ - "orders": { - "order_id": InferredNullability.NON_NULL, - "first_name": InferredNullability.UNKNOWN, - "last_name": InferredNullability.UNKNOWN, - "status": InferredNullability.UNKNOWN, - "active": InferredNullability.UNKNOWN, - "created_at": InferredNullability.UNKNOWN, - "to_date": InferredNullability.UNKNOWN, - "payload": InferredNullability.UNKNOWN, - } - }, - column_types_by_table={ - "orders": { - "order_id": "NUMBER(38,0)", - "first_name": "VARCHAR(16777216)", - "last_name": "VARCHAR(16777216)", - "status": "VARCHAR(16777216)", - "active": "BOOLEAN", - "created_at": "TIMESTAMP_NTZ", - "to_date": "INTEGER", - "payload": "BINARY", - } - }, - inference_profile=ExpressionInferenceProfile( - sql_analysis_dialect="snowflake", - function_return_types={"TO_DATE": "DATE"}, - ), - expected_columns=( - InferredColumn( - name="source_name", - nullability=InferredNullability.NON_NULL, - ), - InferredColumn( - name="active", - type="BOOLEAN", - nullability=InferredNullability.NON_NULL, - ), - InferredColumn(name="selected", type="BOOLEAN"), - InferredColumn(name="full_name", type="TEXT"), - InferredColumn(name="status", type="VARCHAR(16777216)"), - InferredColumn(name="maybe_status", type="VARCHAR(16777216)"), - InferredColumn(name="case_status", type="VARCHAR(16777216)"), - InferredColumn(name="clean_status", type="VARCHAR(16777216)"), - InferredColumn(name="first_created_at", type="TIMESTAMP_NTZ"), - InferredColumn(name="to_date", type="INTEGER"), - InferredColumn(name="merged_payload"), - InferredColumn(name="sliced_payload", type="BINARY"), - ), - expected_lineage_columns=(), - expected_has_star=False, - ) - ], - ids=lambda case: case.description, -) -def test_given_typed_cte_expressions_when_recovering_facts_then_safe_result_types_are_preserved( - test_case: PolyglotAnalysisTestCase, -) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - column_nullability_by_table=test_case.column_nullability_by_table, - column_types_by_table=test_case.column_types_by_table, - inference_profile=test_case.inference_profile, - allow_compact_analysis=True, - recover_cte_facts=True, - ) - - assert result.columns == test_case.expected_columns - - -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="keeps star lineage conservative on fast path", - query_sql='SELECT * FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={ - "orders": { - "order_id": InferredNullability.NON_NULL, - "status": InferredNullability.NULLABLE, - } - }, - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=True, - ) - ], - ids=lambda case: case.description, -) -def test_given_star_projection_when_compact_analysis_disabled_then_marks_star_without_expansion( - test_case: PolyglotAnalysisTestCase, -) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - column_nullability_by_table=test_case.column_nullability_by_table, - ) - - assert result.analysis_succeeded - assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns - assert result.has_star is test_case.expected_has_star - - -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="expands star lineage on rich compact path", - query_sql='SELECT * FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - column_nullability_by_table={ - "orders": { - "order_id": InferredNullability.NON_NULL, - "status": InferredNullability.NULLABLE, - } - }, - expected_columns=( - InferredColumn(name="order_id", nullability=InferredNullability.NON_NULL), - InferredColumn(name="status", nullability=InferredNullability.NULLABLE), - ), - expected_lineage_columns=( - CompiledLineageColumnFact( - output_column="order_id", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="order_id", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - CompiledLineageColumnFact( - output_column="status", - upstream_columns=( - CompiledLineageSourceFact( - resource_type=CompiledResourceType.MODEL, - resource_name="orders", - column_name="status", - ), - ), - transform_kind=ColumnTransformKind.DIRECT, - confidence=ColumnLineageConfidence.HIGH, - ), - ), - expected_has_star=True, - ), PolyglotAnalysisTestCase( description="expands declared table function output columns on rich compact path", query_sql='SELECT * FROM __table_fn("customer_orders")(42)', @@ -1966,7 +903,7 @@ def test_given_star_projection_when_compact_analysis_disabled_then_marks_star_wi def test_given_star_projection_when_compact_analysis_enabled_then_expands_schema_lineage( test_case: PolyglotAnalysisTestCase, ) -> None: - result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( + result: NativeQueryColumns = _reanalysed( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, @@ -1974,148 +911,11 @@ def test_given_star_projection_when_compact_analysis_enabled_then_expands_schema allow_compact_analysis=True, ) - assert result.analysis_succeeded + assert result.succeeded assert result.columns == test_case.expected_columns - assert result.lineage_columns == test_case.expected_lineage_columns assert result.has_star is test_case.expected_has_star -@pytest.mark.parametrize( - "test_case", - [ - PolyglotAnalysisTestCase( - description="matches AST fallback for unqualified single ref", - query_sql='SELECT order_id FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="matches AST fallback for qualified join refs", - query_sql=( - 'SELECT o.order_id, c.name FROM __ref("orders") o ' - 'JOIN __ref("customers") c ON o.customer_id = c.customer_id' - ), - references=( - CompileSqlReference(SqlReferenceKind.REF, "orders"), - CompileSqlReference(SqlReferenceKind.REF, "customers"), - ), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="matches AST fallback for source refs", - query_sql='SELECT payment_id FROM __source("stripe__payments")', - references=(CompileSqlReference(SqlReferenceKind.SOURCE, "stripe__payments"),), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="matches AST fallback for seed refs", - query_sql='SELECT lookup_id FROM __seed("order_statuses")', - references=(CompileSqlReference(SqlReferenceKind.SEED, "order_statuses"),), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="matches AST fallback for casted ref column", - query_sql='SELECT CAST(order_id AS BIGINT) AS order_id FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - PolyglotAnalysisTestCase( - description="matches AST fallback for arithmetic expression fallback", - query_sql='SELECT amount + tax AS total FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - expected_columns=(), - expected_lineage_columns=(), - expected_has_star=False, - ), - ], - ids=lambda case: case.description, -) -def test_given_compact_query_analysis_safe_shape_when_analyzing_then_matches_ast_facts( - test_case: PolyglotAnalysisTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - compact_result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - allow_compact_analysis=True, - ) - assert compact_result.analysis_succeeded - assert compact_result.has_star is test_case.expected_has_star - - polyglot_module: object | None = import_polyglot_sql() - assert polyglot_module is not None - - def raise_compact_error(*args: object, **kwargs: object) -> object: - del args, kwargs - raise ParseError("compact analysis disabled") - - monkeypatch.setattr(polyglot_module, "analyze_query", raise_compact_error) - fallback_result: PolyglotAnalysisResult = analyze_columns_and_lineage_with_polyglot( - query_sql=test_case.query_sql, - references=test_case.references, - allow_compact_analysis=True, - ) - - assert fallback_result.analysis_succeeded - assert compact_result.columns == fallback_result.columns - assert compact_result.has_star == fallback_result.has_star - compact_lineage_columns: tuple[CompiledLineageColumnFact, ...] = tuple( - compact_result.lineage_columns - ) - fallback_lineage_columns: tuple[CompiledLineageColumnFact, ...] = tuple( - fallback_result.lineage_columns - ) - assert len(compact_lineage_columns) == len(fallback_lineage_columns) - for compact_fact, fallback_fact in zip( - compact_lineage_columns, - fallback_lineage_columns, - strict=True, - ): - assert compact_fact.output_column == fallback_fact.output_column - assert compact_fact.upstream_columns == fallback_fact.upstream_columns - assert compact_fact.transform_kind == fallback_fact.transform_kind - - -@pytest.mark.parametrize( - "test_case", - [ - UnexpectedAnalysisFailureTestCase( - description="unexpected native integration failure", - expected_error="unexpected native integration failure", - ) - ], - ids=lambda case: case.description, -) -def test_given_unexpected_polyglot_failure_when_analyzing_then_failure_is_not_silenced( - test_case: UnexpectedAnalysisFailureTestCase, - monkeypatch: pytest.MonkeyPatch, -) -> None: - polyglot_module: object = import_polyglot_sql() - - def raise_unexpected_failure(*args: object, **kwargs: object) -> object: - del args, kwargs - raise RuntimeError(test_case.expected_error) - - monkeypatch.setattr(polyglot_module, "analyze_query", raise_unexpected_failure) - - with pytest.raises(RuntimeError, match=test_case.expected_error): - analyze_columns_and_lineage_with_polyglot( - query_sql='SELECT id FROM __ref("orders")', - references=(CompileSqlReference(SqlReferenceKind.REF, "orders"),), - allow_compact_analysis=True, - ) - - @pytest.mark.parametrize( "test_case", [ From a247b9c5d32f760e8588d9075a1ecc8e3e18bf1e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:39:04 +0100 Subject: [PATCH 16/36] feat(native)!: assemble every SQL test and scan every bridged macro call natively SQL-test assembly no longer hands tests to Python: non-ASCII contents (Python casefold, \w and re.IGNORECASE keys from generated tables), deep CTE trees, decimal rounding/underflow/overflow under Python's default context, reference and macro scan errors are native. Tests mocking macros keep their model queries from the user's macros, reported as the sql_test_glue.macro_mock_queries extension callback. The macro bridge scans every string natively and reports where Python's scan raises; strings with an unresolvable macro tree or a scan error evaluate their calls in order without the memo. Deletes Python's SQL-test assembly, scope deps and case identity. --- .../compiler/_helpers/sql_tests/assembly.rs | 52 +- .../_helpers/sql_tests/case_identity.rs | 126 +- .../_helpers/sql_tests/macro_call_names.rs | 108 +- .../compiler/_helpers/sql_tests/mock_reads.rs | 252 +-- .../sqlbuild-analysis/src/compiler/models.rs | 47 +- .../tests/test_sql_test_helper_references.rs | 72 +- .../src/text/_helpers/case_tables.rs | 17 + .../src/text/main/active_python_text.rs | 3 +- crates/sqlbuild-core/src/text/main/mod.rs | 1 + .../src/text/main/python_ignorecase_key.rs | 18 + .../src/text/main/python_text.rs | 7 +- crates/sqlbuild-core/src/text/models.rs | 2 + .../text/re_ignorecase_keys/unicode_15_0_0.in | 1463 ++++++++++++++++ .../text/re_ignorecase_keys/unicode_15_1_0.in | 1463 ++++++++++++++++ .../text/re_ignorecase_keys/unicode_16_0_0.in | 1490 +++++++++++++++++ .../src/text/tests/test_python_compat.rs | 31 + .../_helpers/render/macro_call_rows.rs | 5 +- .../bindings/_helpers/render/macro_calls.rs | 31 +- .../_helpers/sql_test_glue/assembly.rs | 35 +- .../src/macro_calls/_helpers/scanner.rs | 99 +- .../macro_calls/main/scan_macro_call_sites.rs | 46 +- .../sqlbuild-render/src/macro_calls/models.rs | 42 +- .../tests/test_scan_macro_call_sites.rs | 165 +- .../src/macro_calls/tests/test_types.rs | 16 +- .../native_fallbacks.toml | 36 +- scripts/python_char_table/_helpers/table.py | 45 + scripts/python_char_table/constants.py | 1 + scripts/python_char_table/main/generate.py | 7 + src/sqlbuild/_native.pyi | 2 +- .../compile/_helpers/assembly/project.py | 172 +- .../_helpers/macro_bridge/expansion.py | 122 +- .../_helpers/native_stages/sql_tests.py | 48 +- .../compile/_helpers/sql_tests/helper_ctes.py | 154 -- .../compile/_helpers/sql_tests/identity.py | 46 - .../compile/_helpers/sql_tests/scope_deps.py | 67 - src/sqlbuild/compiler/frontier/types.py | 7 +- .../macro_bridge/classes/macro_bridge.py | 46 +- src/sqlbuild/compiler/macro_bridge/models.py | 19 +- .../sql_test_glue/_helpers/assembly.py | 25 +- .../sql_test_glue/_helpers/deferrals.py | 24 - .../compiler/sql_test_glue/constants.py | 4 +- .../main/_assemble_native_sql_tests.py | 6 +- src/sqlbuild/compiler/sql_test_glue/models.py | 15 +- src/sqlbuild/compiler/sql_test_glue/types.py | 7 +- .../scripts/compiler_differential/helpers.py | 6 +- .../test_native_fallback_gate.py | 8 +- ...rameter_the_default_context_overflows.json | 10 + ...r_a_model_with_a_non_ascii_name_after.json | 10 + ...lper_whose_nested_cte_shadows_another.json | 10 + ...helper_with_an_invalid_reference_call.json | 10 + ...he_default_context_rounds_and_flushes.json | 10 + ...meterized_and_mock_reads_helper_tests.json | 34 + ..._folded_reads_and_located_diagnostics.json | 10 + .../compiler/sql_test_glue/_test_types.py | 11 +- .../compiler/sql_test_glue/helpers.py | 144 +- .../test_native_sql_test_assembly.py | 107 +- .../frontier/test_native_fallback_recorder.py | 4 +- .../macro_bridge/classes/test_macro_bridge.py | 9 +- 58 files changed, 5760 insertions(+), 1067 deletions(-) create mode 100644 crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs create mode 100644 crates/sqlbuild-core/src/text/re_ignorecase_keys/unicode_15_0_0.in create mode 100644 crates/sqlbuild-core/src/text/re_ignorecase_keys/unicode_15_1_0.in create mode 100644 crates/sqlbuild-core/src/text/re_ignorecase_keys/unicode_16_0_0.in delete mode 100644 src/sqlbuild/compiler/compile/_helpers/sql_tests/identity.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py delete mode 100644 src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py create mode 100644 tests/goldens/python_oracle/sql_test_assembly_a_decimal_case_parameter_the_default_context_overflows.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_a_mock_reading_a_deeply_nested_helper_whose_nested_cte_shadows_another.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_an_unread_helper_with_an_invalid_reference_call.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_decimal_case_parameters_the_default_context_rounds_and_flushes.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_model_direct_parameterized_and_mock_reads_helper_tests.json create mode 100644 tests/goldens/python_oracle/sql_test_assembly_non_ascii_test_contents_case_folded_reads_and_located_diagnostics.json diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs index 6836d3cf0f..8223bb690f 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs @@ -3,13 +3,13 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; -use crate::compiler::_helpers::sql_tests::case_identity::case_fingerprint; +use crate::compiler::_helpers::sql_tests::case_identity::{FingerprintFailure, case_fingerprint}; use crate::compiler::_helpers::sql_tests::macro_call_names::macro_call_names; use crate::compiler::_helpers::sql_tests::mock_reads::{ MockReadsRequest, mock_reading_helper_diagnostics, }; use crate::compiler::models::{ - AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyDeferral, SqlTestAssemblyModel, + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyFailure, SqlTestAssemblyModel, SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyTest, SqlTestHelperDiagnostic, }; @@ -19,7 +19,7 @@ const MACRO_MODE: &str = "macro"; const UDF_MODE: &str = "udf"; const UDF_REFERENCE_KIND: &str = "udf"; -/// Each test's facts in request order, or the reason Python assembles it. +/// Each test's facts in request order, or why its assembly raises. pub(crate) fn assemble_batch(batch: &SqlTestAssemblyBatch) -> Vec { let tests_macros = batch.tests.iter().any(|test| { matches!(&test.payload, SqlTestAssemblyPayload::Direct { mode, .. } if mode == MACRO_MODE) @@ -42,14 +42,15 @@ pub(crate) fn assemble_batch(batch: &SqlTestAssemblyBatch) -> Vec SqlTestAssemblyOutcome::Assembled(facts), - Err(deferral) => SqlTestAssemblyOutcome::Deferred(deferral), + Ok(outcome) => outcome, + Err(failure) => SqlTestAssemblyOutcome::Failed(failure), }) .collect() } -/// A model's macro dependencies, or Python's scan of its pre-macro SQL when none were recorded. -type MacroNames = Result, SqlTestAssemblyDeferral>; +/// A model's macro dependencies, or Python's scan of its pre-macro SQL when none were recorded: +/// the names, or the message of the `CompileInputError` the scan raises. +type MacroNames = Result, String>; struct Project<'a> { models: &'a [SqlTestAssemblyModel], @@ -60,7 +61,7 @@ struct Project<'a> { fn assemble( test: &SqlTestAssemblyTest, project: &Project<'_>, -) -> Result { +) -> Result { let models = project.models; let syntax = project.syntax; let mut target_model_names: Vec = Vec::new(); @@ -78,9 +79,6 @@ fn assemble( direct_scope_deps(mode, tested_resource_names, project)? } SqlTestAssemblyPayload::Model(payload) => { - if payload.has_macro_mocks { - return Err(SqlTestAssemblyDeferral::MacroMocks); - } for name in payload .expected_model_names .iter() @@ -104,16 +102,23 @@ fn assemble( .collect() } }; + let mut overflow = false; let case_fingerprint = match &test.case_name { - Some(case_name) => Some(case_fingerprint( - test, - case_name, - &scope_deps, - &tested_resources, - )?), + Some(case_name) => { + match case_fingerprint(test, case_name, &scope_deps, &tested_resources) { + Ok(fingerprint) => Some(fingerprint), + Err(FingerprintFailure::DecimalOverflow) => { + overflow = true; + None + } + Err(FingerprintFailure::Internal(reason)) => { + return Err(SqlTestAssemblyFailure::Internal(reason)); + } + } + } None => None, }; - Ok(AssembledSqlTestFacts { + let facts = AssembledSqlTestFacts { name: test_name(test), scope_deps, target_model_names, @@ -122,6 +127,11 @@ fn assemble( .unwrap_or(&test.relative_stem) .to_owned(), diagnostics, + }; + Ok(if overflow { + SqlTestAssemblyOutcome::FingerprintOverflow(facts) + } else { + SqlTestAssemblyOutcome::Assembled(facts) }) } @@ -143,12 +153,14 @@ fn direct_scope_deps( mode: &str, tested_names: &[String], project: &Project<'_>, -) -> Result, SqlTestAssemblyDeferral> { +) -> Result, SqlTestAssemblyFailure> { let mut scope_deps: Vec<(&'static str, String)> = Vec::new(); match mode { MACRO_MODE => { for (model, names) in project.models.iter().zip(project.macro_names) { - let names = names.as_ref().map_err(|deferral| *deferral)?; + let names = names + .as_ref() + .map_err(|message| SqlTestAssemblyFailure::Input(message.clone()))?; if names.iter().any(|name| tested_names.contains(name)) { scope_deps.push((MODEL_RESOURCE, model.name.clone())); } diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs index 4bbc98f63f..2a20c67fd0 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs @@ -4,14 +4,23 @@ use sha2::{Digest, Sha256}; use sqlbuild_core::json::main::dumps::dumps; use sqlbuild_core::json::models::{JsonDialect, JsonInteger, JsonValue, StdlibJsonOptions}; -use crate::compiler::models::{ - SqlTestAssemblyDeferral, SqlTestAssemblyTest, SqlTestParameterValue, -}; +use crate::compiler::models::{SqlTestAssemblyTest, SqlTestParameterValue}; + +/// Why Python's fingerprint of a case raises. +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum FingerprintFailure { + /// `Decimal.normalize()` overflows the default context: Python raises `decimal.Overflow`. + DecimalOverflow, + /// An internal native failure. + Internal(String), +} /// Python's default decimal context: precision and exponent limits `normalize()` applies. const DECIMAL_PRECISION: usize = 28; const DECIMAL_EMAX: i64 = 999_999; const DECIMAL_EMIN: i64 = -999_999; +/// The smallest exponent of a subnormal result: `Emin - prec + 1`. +const DECIMAL_ETINY: i64 = DECIMAL_EMIN - 27; /// The SHA-256 of Python's compact, ASCII, key-sorted JSON of the case's compile inputs. pub(crate) fn case_fingerprint( @@ -19,7 +28,7 @@ pub(crate) fn case_fingerprint( case_name: &str, scope_deps: &[(&'static str, String)], tested_resources: &[(String, String)], -) -> Result { +) -> Result { let mut sorted_deps: Vec<(&str, &str)> = scope_deps .iter() .map(|(kind, name)| (*kind, name.as_str())) @@ -34,7 +43,7 @@ pub(crate) fn case_fingerprint( .parameter_values .iter() .map(|(name, value)| Ok(JsonValue::Array(vec![string(name), identity(value)?]))) - .collect::>()?; + .collect::>()?; let payload = JsonValue::Object(vec![ ("source_path".to_owned(), string(&test.relative_path)), ("block_index".to_owned(), integer(test.block_index)), @@ -70,8 +79,11 @@ pub(crate) fn case_fingerprint( sort_keys: true, allow_nan: true, }); - let encoded = - dumps(&payload, &dialect).map_err(|_| SqlTestAssemblyDeferral::UnencodableValue)?; + let encoded = dumps(&payload, &dialect).map_err(|error| { + FingerprintFailure::Internal(format!( + "the case fingerprint payload does not encode: {error:?}" + )) + })?; Ok(Sha256::digest(encoded.as_bytes()) .iter() .map(|byte| format!("{byte:02x}")) @@ -96,7 +108,7 @@ fn pairs(entries: &[(&str, &str)]) -> JsonValue { } /// Python's `sql_value_identity`: `(kind, payload)` with decimals as normalized tuples. -fn identity(value: &SqlTestParameterValue) -> Result { +fn identity(value: &SqlTestParameterValue) -> Result { let (kind, payload) = match value { SqlTestParameterValue::String(text) => ("string", string(text)), SqlTestParameterValue::Integer(number) => ("integer", integer(*number)), @@ -116,53 +128,113 @@ fn identity(value: &SqlTestParameterValue) -> Result>()?, + .collect::>()?, ), ), }; Ok(JsonValue::Array(vec![string(kind), payload])) } -fn identities(items: &[SqlTestParameterValue]) -> Result { +fn identities(items: &[SqlTestParameterValue]) -> Result { Ok(JsonValue::Array( items.iter().map(identity).collect::>()?, )) } -/// `Decimal.normalize().as_tuple()` where the default context neither rounds nor clamps. +/// `Decimal.normalize().as_tuple()` under Python's default context: rounded half-even to its +/// precision, or to the subnormal exponent floor, then stripped of trailing zeros. fn normalized_decimal( negative: bool, digits: &[u8], exponent: i64, -) -> Result { +) -> Result { let sign = integer(i64::from(negative)); - let first = digits.iter().position(|digit| *digit != 0); - let Some(first) = first else { - return Ok(JsonValue::Array(vec![ - sign, + let zero = || { + JsonValue::Array(vec![ + integer(i64::from(negative)), JsonValue::Array(vec![integer(0)]), integer(0), - ])); + ]) + }; + let Some(first) = digits.iter().position(|digit| *digit != 0) else { + return Ok(zero()); }; - let significant = &digits[first..]; - let adjusted = exponent + i64::try_from(significant.len()).unwrap_or(i64::MAX) - 1; - if significant.len() > DECIMAL_PRECISION || !(DECIMAL_EMIN..=DECIMAL_EMAX).contains(&adjusted) { - return Err(SqlTestAssemblyDeferral::DecimalContext); + let mut coefficient: Vec = digits[first..].to_vec(); + let mut exponent: i64 = exponent; + let length = |coefficient: &[u8]| i64::try_from(coefficient.len()).unwrap_or(i64::MAX); + let adjusted: i64 = exponent.saturating_add(length(&coefficient) - 1); + let dropped: i64 = if adjusted < DECIMAL_EMIN { + DECIMAL_ETINY.saturating_sub(exponent).max(0) + } else { + (length(&coefficient) - i64::try_from(DECIMAL_PRECISION).unwrap_or(i64::MAX)).max(0) + }; + if dropped > 0 { + coefficient = + rounded_half_even(&coefficient, usize::try_from(dropped).unwrap_or(usize::MAX)); + exponent = exponent.saturating_add(dropped); + if coefficient.len() > DECIMAL_PRECISION { + let _ = coefficient.pop(); + exponent = exponent.saturating_add(1); + } + if coefficient.iter().all(|digit| *digit == 0) { + return Ok(zero()); + } + } + if exponent.saturating_add(length(&coefficient) - 1) > DECIMAL_EMAX { + return Err(FingerprintFailure::DecimalOverflow); } - let mut end = significant.len(); - let mut normalized_exponent = exponent; - while significant[end - 1] == 0 && normalized_exponent < DECIMAL_EMAX { + let mut end = coefficient.len(); + while coefficient[end - 1] == 0 && exponent < DECIMAL_EMAX { end -= 1; - normalized_exponent += 1; + exponent += 1; } Ok(JsonValue::Array(vec![ sign, JsonValue::Array( - significant[..end] + coefficient[..end] .iter() .map(|digit| integer(i64::from(*digit))) .collect(), ), - integer(normalized_exponent), + integer(exponent), ])) } + +/// The coefficient without its last `dropped` digits, rounded half to even; a carry may add a +/// leading digit. +fn rounded_half_even(coefficient: &[u8], dropped: usize) -> Vec { + if dropped > coefficient.len() { + return vec![0]; + } + let (kept, removed) = coefficient.split_at(coefficient.len() - dropped); + let mut kept: Vec = if kept.is_empty() { + vec![0] + } else { + kept.to_vec() + }; + let round_up: bool = match removed.first() { + Some(6..=9) => true, + Some(5) => { + removed[1..].iter().any(|digit| *digit != 0) + || kept.last().is_some_and(|digit| digit % 2 == 1) + } + _ => false, + }; + if round_up { + let mut index = kept.len(); + loop { + if index == 0 { + kept.insert(0, 1); + break; + } + index -= 1; + if kept[index] == 9 { + kept[index] = 0; + } else { + kept[index] += 1; + break; + } + } + } + kept +} diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs index 38c0421c61..3c780ca3ce 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs @@ -1,105 +1,103 @@ //! Python's `find_macro_call_names`: unique top-level macro call names in encounter order. +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_alnum::is_python_alnum; +use sqlbuild_core::text::main::is_python_alpha::is_python_alpha; +use sqlbuild_core::text::main::is_python_space::is_python_space; +use sqlbuild_core::text::models::PythonText; use sqlbuild_sqltext::sql_scan::main::matching_paren::matching_paren; use sqlbuild_sqltext::sql_scan::main::non_code_end::non_code_end; -use sqlbuild_sqltext::sql_scan::models::QuotePolicy; - -use crate::compiler::models::SqlTestAssemblyDeferral; +use sqlbuild_sqltext::sql_scan::models::{QuotePolicy, Unclosed}; /// Declaration calls that expansion substitutes rather than running as macros. const DECLARATION_CALLS: [&str; 2] = ["enum", "const"]; +const MISSING_PARENTHESIS: &str = "expected opening parenthesis"; -type Scan = Result; - -/// The names Python's scan returns; text it raises on, or classifies by Unicode, is Python's. -pub(crate) fn macro_call_names(sql: &str) -> Scan> { +/// The names Python's scan returns, or the message of the `CompileInputError` it raises. +pub(crate) fn macro_call_names(sql: &str) -> Result, String> { + let python: PythonText = active_python_text(); let bytes = sql.as_bytes(); let mut names: Vec = Vec::new(); let mut index = 0; while index < bytes.len() { - match non_code_end(bytes, index, QuotePolicy::COMPILER) { - Ok(Some(end)) => { - index = end; - continue; - } - Ok(None) => {} - Err(_) => return Err(SqlTestAssemblyDeferral::MacroCallScan), + if let Some(end) = non_code_end(bytes, index, QuotePolicy::COMPILER).map_err(unclosed)? { + index = end; + continue; } - if bytes[index] != b'@' || !macro_call_at(bytes, index)? { + if bytes[index] != b'@' || !macro_call_at(python, sql, index) { index += 1; continue; } - let name_end = identifier_end(bytes, index + 1)?; + let name_end = identifier_end(python, sql, index + 1); let name = &sql[index + 1..name_end]; if !names.iter().any(|known| known == name) { names.push(name.to_owned()); } - let open = space_end(bytes, name_end)?; + let open = space_end(sql, name_end); if bytes.get(open) != Some(&b'(') { - return Err(SqlTestAssemblyDeferral::MacroCallScan); + return Err(MISSING_PARENTHESIS.to_owned()); } - index = matching_paren(bytes, open, QuotePolicy::COMPILER) - .map_err(|_| SqlTestAssemblyDeferral::MacroCallScan)? - + 1; + index = matching_paren(bytes, open, QuotePolicy::COMPILER).map_err(unclosed)? + 1; } names.retain(|name| !DECLARATION_CALLS.contains(&name.as_str())); Ok(names) } +/// The message of the `CompileInputError` Python's macro scan raises for unclosed text. +fn unclosed(error: Unclosed) -> String { + let what = match error { + Unclosed::BlockComment => "block comment", + Unclosed::Quote => "quoted string", + Unclosed::Parenthesis => "parenthesis", + }; + format!("Macro expansion contains an unclosed {what}") +} + /// Python's `_is_macro_call_start`, which skips whitespace after each name character. -fn macro_call_at(bytes: &[u8], at: usize) -> Scan { - let Some(&first) = bytes.get(at + 1) else { - return Ok(false); +fn macro_call_at(python: PythonText, sql: &str, at: usize) -> bool { + let Some(first) = char_at(sql, at + 1) else { + return false; }; - if !ascii(first)? || !(first == b'_' || first.is_ascii_alphabetic()) { - return Ok(false); + if !(first == '_' || is_python_alpha(python, first)) { + return false; } - let mut cursor = at + 2; - while let Some(&byte) = bytes.get(cursor) { - if !ascii(byte)? || !is_identifier_continue(byte) { + let mut cursor = at + 1 + first.len_utf8(); + while let Some(character) = char_at(sql, cursor) { + if !is_identifier_continue(python, character) { break; } - cursor = space_end(bytes, cursor + 1)?; + cursor = space_end(sql, cursor + character.len_utf8()); } - Ok(bytes.get(cursor) == Some(&b'(')) + sql.as_bytes().get(cursor) == Some(&b'(') } -fn identifier_end(bytes: &[u8], start: usize) -> Scan { +fn identifier_end(python: PythonText, sql: &str, start: usize) -> usize { let mut cursor = start; - while let Some(&byte) = bytes.get(cursor) { - if !ascii(byte)? || !is_identifier_continue(byte) { + while let Some(character) = char_at(sql, cursor) { + if !is_identifier_continue(python, character) { break; } - cursor += 1; + cursor += character.len_utf8(); } - Ok(cursor) + cursor } -/// Python's `str.isspace` skip; a non-ASCII character there is classified by Python. -fn space_end(bytes: &[u8], start: usize) -> Scan { +/// Python's `str.isspace` skip. +fn space_end(sql: &str, start: usize) -> usize { let mut cursor = start; - while let Some(&byte) = bytes.get(cursor) { - if !ascii(byte)? - || !matches!( - byte, - b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c | 0x1c..=0x1f - ) - { + while let Some(character) = char_at(sql, cursor) { + if !is_python_space(character) { break; } - cursor += 1; + cursor += character.len_utf8(); } - Ok(cursor) + cursor } -fn is_identifier_continue(byte: u8) -> bool { - byte == b'_' || byte.is_ascii_alphanumeric() +fn is_identifier_continue(python: PythonText, character: char) -> bool { + character == '_' || is_python_alnum(python, character) } -fn ascii(byte: u8) -> Scan { - if byte.is_ascii() { - Ok(true) - } else { - Err(SqlTestAssemblyDeferral::MacroCallScan) - } +fn char_at(sql: &str, index: usize) -> Option { + sql.get(index..).and_then(|rest| rest.chars().next()) } diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs index 5e9793b5e8..8897fd9565 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs @@ -2,17 +2,23 @@ use std::collections::HashSet; +use polyglot_sql::expressions::With; use polyglot_sql::{ ComplexityGuardOptions, Dialect, DialectType, Expression, ExpressionWalk, ParseOptions, }; -use serde_json::Value; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_space::is_python_space; +use sqlbuild_core::text::main::is_python_word::is_python_word; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::main::python_ignorecase_key::python_ignorecase_key; +use sqlbuild_core::text::models::PythonText; use sqlbuild_sqltext::sql_references::main::extract_sql_references::extract_sql_references; use sqlbuild_sqltext::sql_references::models::{ReferenceExtraction, SqlReference}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; use crate::compiler::_helpers::sql_tests::planning::MOCK_CTE_PREFIXES; use crate::compiler::models::{ - SqlTestAssemblyDeferral, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, + SqlTestAssemblyFailure, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestHelperDiagnostic, }; @@ -26,12 +32,10 @@ const RELATION_KINDS: [&str; 5] = [ DBT_REF_KIND, TABLE_FUNCTION_KIND, ]; -/// Payload nesting past which the parsed tree is left to Python's `to_dict` walk. -const MAX_TREE_DEPTH: usize = 400; /// The function call depth SQLBuild's Polyglot proxy allows `parse_one`. const MAX_FUNCTION_CALL_DEPTH: usize = 512; -type Deferrable = Result; +type Deferrable = Result; /// A reference as compile carries it: the scanner's or a model input's. #[derive(Clone)] @@ -152,13 +156,12 @@ pub(crate) fn mock_reading_helper_diagnostics( .into_iter() .find(|reference| RELATION_KINDS.contains(&reference.kind.as_str())); if let Some(reference) = first { - referencing.assign(ascii_fold(&cte.name)?, reference); + referencing.assign(fold(&cte.name), reference); } } if referencing.entries.is_empty() { return Ok(Vec::new()); } - require_ascii(request.test, payload)?; let readers: Vec<&SqlTestCte> = payload .expected_ctes .iter() @@ -218,42 +221,19 @@ fn is_mock_name(name: &str) -> bool { .any(|prefix| name.starts_with(prefix)) } -/// Python's `str.casefold`, exact only on ASCII text. -fn ascii_fold(text: &str) -> Deferrable { - if text.is_ascii() { - Ok(text.to_ascii_lowercase()) - } else { - Err(SqlTestAssemblyDeferral::NonAsciiText) - } +/// Python's `str.casefold`. +fn fold(text: &str) -> String { + python_casefold(active_python_text(), text) } -/// Python's folding, token and header patterns read only ASCII text exactly. -fn require_ascii( - test: &SqlTestAssemblyTest, - payload: &SqlTestAssemblyModelPayload, -) -> Deferrable<()> { - let ascii = test.contents.is_ascii() - && test.block_sql.is_ascii() - && payload - .authored_ctes - .iter() - .chain(&payload.expected_ctes) - .chain(&payload.assertion_ctes) - .all(|cte| cte.name.is_ascii() && cte.sql_body.is_ascii()); - if ascii { - Ok(()) - } else { - Err(SqlTestAssemblyDeferral::NonAsciiText) - } -} - -/// Python's `scan_sql_reference_calls(...).references`; a failing scan is Python's to raise. +/// Python's `scan_sql_reference_calls(...).references`; a failing scan raises its unlocated +/// `CompileInputError`. fn references(sql: &str, syntax: &LexicalSyntax) -> Deferrable> { match extract_sql_references(sql, syntax) { ReferenceExtraction::Extracted(scan) => { Ok(scan.references.into_iter().map(scanned).collect()) } - ReferenceExtraction::Failed(_) => Err(SqlTestAssemblyDeferral::ReferenceScan), + ReferenceExtraction::Failed(failure) => Err(SqlTestAssemblyFailure::Input(failure.message)), } } @@ -277,7 +257,7 @@ fn model_reference(reference: &SqlTestAssemblyReference) -> Reference { fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferrable> { let mut ctes: OrderedEntries<&'a SqlTestCte> = OrderedEntries::new(); for cte in authored { - ctes.assign(ascii_fold(&cte.name)?, cte); + ctes.assign(fold(&cte.name), cte); } let keys: Vec = ctes.keys(); let mut reads: OrderedEntries> = OrderedEntries::new(); @@ -310,7 +290,7 @@ fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferra /// Python's `_parsed_reads`: unqualified reads of `keys` no nested CTE shadows, or None unparsed. fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { - let folded = ascii_fold(sql)?; + let folded = fold(sql); if !keys.iter().any(|key| folded.contains(key.as_str())) { return Ok(Some(Vec::new())); } @@ -323,7 +303,7 @@ fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { return Ok(None); } let parsed: Expression = statements.remove(0); - let nested = defined_cte_keys(&parsed)?; + let nested = defined_cte_keys(&parsed); let mut reads: Vec = Vec::new(); for node in parsed.dfs() { let Expression::Table(table) = node else { @@ -332,7 +312,7 @@ fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { if table.schema.is_some() || table.catalog.is_some() { continue; } - let key = ascii_fold(&table.name.name)?; + let key = fold(&table.name.name); if keys.contains(&key) && !nested.contains(&key) && !reads.contains(&key) { reads.push(key); } @@ -345,61 +325,55 @@ fn proxy_parse_options() -> Deferrable { let guard: ComplexityGuardOptions = serde_json::from_value(serde_json::json!({ "maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH, })) - .map_err(|_| SqlTestAssemblyDeferral::UnreadableTree)?; + .map_err(|error| SqlTestAssemblyFailure::Internal(error.to_string()))?; Ok(ParseOptions { complexity_guard: Some(guard), }) } -/// Python's `_defined_cte_keys` over the serialized tree Python's `to_dict` returns. -fn defined_cte_keys(parsed: &Expression) -> Deferrable> { - let tree = serde_json::to_value(parsed).map_err(|_| SqlTestAssemblyDeferral::UnreadableTree)?; +/// Python's `_defined_cte_keys` over `to_dict()`: the folded name of every CTE in a `ctes` list, +/// which only `WITH` clauses hold, at any depth. +fn defined_cte_keys(parsed: &Expression) -> HashSet { let mut keys: HashSet = HashSet::new(); - let mut pending: Vec<(&Value, usize)> = vec![(&tree, 0)]; - while let Some((value, depth)) = pending.pop() { - if depth > MAX_TREE_DEPTH { - return Err(SqlTestAssemblyDeferral::UnreadableTree); - } - match value { - Value::Array(items) => pending.extend(items.iter().map(|item| (item, depth + 1))), - Value::Object(entries) => { - if let Some(Value::Array(ctes)) = entries.get("ctes") { - for cte in ctes { - if let Some(Value::String(name)) = cte - .get("alias") - .filter(|alias| alias.is_object()) - .and_then(|alias| alias.get("name")) - { - let _ = keys.insert(ascii_fold(name)?); - } - } - } - pending.extend(entries.values().map(|item| (item, depth + 1))); - } - _ => {} + for node in parsed.dfs() { + let with: Option<&With> = match node { + Expression::Select(select) => select.with.as_ref(), + Expression::Union(union) => union.with.as_ref(), + Expression::Intersect(intersect) => intersect.with.as_ref(), + Expression::Except(except) => except.with.as_ref(), + Expression::Pivot(pivot) => pivot.with.as_ref(), + Expression::Insert(insert) => insert.with.as_ref(), + Expression::Update(update) => update.with.as_ref(), + Expression::Delete(delete) => delete.with.as_ref(), + Expression::CreateTable(create) => create.with_cte.as_ref(), + Expression::With(with) => Some(with), + _ => None, + }; + for cte in with.map_or(&[][..], |with| with.ctes.as_slice()) { + let _ = keys.insert(fold(&cte.alias.name)); } } - Ok(keys) + keys } -/// Python's `_token_reads` over `[A-Za-z_][\w$]*` tokens of ASCII text. +/// Python's `_token_reads` over `[A-Za-z_][\w$]*` tokens. fn token_reads(sql: &str, keys: &[String]) -> Vec { - let bytes = sql.as_bytes(); + let python: PythonText = active_python_text(); + let chars: Vec<(usize, char)> = sql.char_indices().collect(); let mut tokens: Vec = Vec::new(); let mut index = 0; - while index < bytes.len() { - if !(bytes[index].is_ascii_alphabetic() || bytes[index] == b'_') { + while index < chars.len() { + if !(chars[index].1.is_ascii_alphabetic() || chars[index].1 == '_') { index += 1; continue; } - let start = index; + let start = chars[index].0; index += 1; - while index < bytes.len() - && (bytes[index].is_ascii_alphanumeric() || matches!(bytes[index], b'_' | b'$')) - { + while index < chars.len() && is_word_or_dollar(python, chars[index].1) { index += 1; } - let token = sql[start..index].to_ascii_lowercase(); + let end = chars.get(index).map_or(sql.len(), |(offset, _)| *offset); + let token = fold(&sql[start..end]); if !tokens.contains(&token) { tokens.push(token); } @@ -433,7 +407,7 @@ fn mock_keys(reference: &Reference) -> Deferrable> { } names .iter() - .map(|name| ascii_fold(&format!("{prefix}{name}"))) + .map(|name| Ok(fold(&format!("{prefix}{name}")))) .collect() } @@ -526,17 +500,25 @@ fn mock_reference_help(mock_name: &str, call: &str, reference: &Reference) -> St format!("Write the rows of '{mock_name}' directly, for example {mock_name} AS (SELECT ...).") } -/// Python's `sql_test_cte_location` and `reference_call_location` on ASCII contents. +/// Python's `sql_test_cte_location` and `reference_call_location`, in code points. fn located(test: &SqlTestAssemblyTest, cte_name: &str, call: &str) -> Span { - let contents = test.contents.as_str(); - let block_offset = contents.find(test.block_sql.as_str()).unwrap_or(0); - let (start, length) = match cte_header(contents, cte_name, block_offset) { - Some((header_start, header_end)) => find_ignoring_case(contents, call, header_end) - .map_or((header_start, cte_name.len()), |found| (found, call.len())), + let python: PythonText = active_python_text(); + let contents: Vec = test.contents.chars().collect(); + let block_offset: usize = test + .contents + .find(test.block_sql.as_str()) + .map_or(0, |byte| test.contents[..byte].chars().count()); + let name: Vec = cte_name.chars().collect(); + let call: Vec = call.chars().collect(); + let (start, length) = match cte_header(python, &contents, &name, block_offset) { + Some((header_start, header_end)) => { + find_ignoring_case(python, &contents, &call, header_end) + .map_or((header_start, name.len()), |found| (found, call.len())) + } None => (block_offset, 0), }; - let (line, column) = line_and_column(contents, start); - let (end_line, end_column) = line_and_column(contents, start + length); + let (line, column) = line_and_column(&contents, start); + let (end_line, end_column) = line_and_column(&contents, start + length); Span { line, column, @@ -545,75 +527,95 @@ fn located(test: &SqlTestAssemblyTest, cte_name: &str, call: &str) -> Span { } } -/// Python's `(? Option<(usize, usize)> { - let bytes = contents.as_bytes(); +/// Python's `(? Option<(usize, usize)> { + let space = |index: usize| contents.get(index).is_some_and(|c| is_python_space(*c)); let mut start = from; - while let Some(found) = find_ignoring_case(contents, name, start) { + while let Some(found) = find_ignoring_case(python, contents, name, start) { start = found + 1; - if found > 0 && is_word_or_dollar(bytes[found - 1]) { + if found > 0 && is_word_or_dollar(python, contents[found - 1]) { continue; } let mut cursor = found + name.len(); - if matches!(bytes.get(cursor), Some(b'"' | b'`')) { + if matches!(contents.get(cursor), Some('"' | '`')) { cursor += 1; } let spaces = cursor; - while bytes - .get(cursor) - .is_some_and(|byte| is_python_ascii_space(*byte)) - { + while space(cursor) { cursor += 1; } - if cursor == spaces - || !bytes - .get(cursor..cursor + 2) - .is_some_and(|keyword| keyword.eq_ignore_ascii_case(b"AS")) - { + if cursor == spaces || !matches_ignoring_case(python, contents, &['a', 's'], cursor) { continue; } cursor += 2; - while bytes - .get(cursor) - .is_some_and(|byte| is_python_ascii_space(*byte)) - { + while space(cursor) { cursor += 1; } - if bytes.get(cursor) == Some(&b'(') { + if contents.get(cursor) == Some(&'(') { return Some((found, cursor + 1)); } } None } -fn find_ignoring_case(contents: &str, needle: &str, from: usize) -> Option { - let haystack = contents.as_bytes(); - let needle = needle.as_bytes(); - if needle.is_empty() { - return (from <= haystack.len()).then_some(from); - } - (from..=haystack.len().checked_sub(needle.len())?) - .find(|&index| haystack[index..index + needle.len()].eq_ignore_ascii_case(needle)) +/// Python's `re.compile(re.escape(needle), re.IGNORECASE).search(contents, from)`. +fn find_ignoring_case( + python: PythonText, + contents: &[char], + needle: &[char], + from: usize, +) -> Option { + let needle: Vec = needle + .iter() + .map(|character| python_ignorecase_key(python, *character)) + .collect(); + (from..=contents.len().checked_sub(needle.len())?) + .find(|&index| keys_at(python, contents, &needle, index)) +} + +/// Whether `needle` matches at `at` under `re.IGNORECASE`. +fn matches_ignoring_case( + python: PythonText, + contents: &[char], + needle: &[char], + at: usize, +) -> bool { + let keys: Vec = needle + .iter() + .map(|character| python_ignorecase_key(python, *character)) + .collect(); + keys_at(python, contents, &keys, at) } -fn is_word_or_dollar(byte: u8) -> bool { - byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$') +fn keys_at(python: PythonText, contents: &[char], keys: &[char], at: usize) -> bool { + contents.get(at..at + keys.len()).is_some_and(|window| { + window + .iter() + .zip(keys) + .all(|(character, key)| python_ignorecase_key(python, *character) == *key) + }) } -/// Python's `\s` on ASCII text, which includes the information separators. -fn is_python_ascii_space(byte: u8) -> bool { - matches!( - byte, - b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c | 0x1c..=0x1f - ) +/// Python's `[\w$]`. +fn is_word_or_dollar(python: PythonText, character: char) -> bool { + character == '$' || is_python_word(python, character) } -fn line_and_column(contents: &str, offset: usize) -> (usize, usize) { - let before = &contents.as_bytes()[..offset]; - let line = before.iter().filter(|byte| **byte == b'\n').count() + 1; +fn line_and_column(contents: &[char], offset: usize) -> (usize, usize) { + let before = &contents[..offset]; + let line = before + .iter() + .filter(|character| **character == '\n') + .count() + + 1; let line_start = before .iter() - .rposition(|byte| *byte == b'\n') + .rposition(|character| *character == '\n') .map_or(0, |index| index + 1); (line, offset - line_start + 1) } diff --git a/crates/sqlbuild-analysis/src/compiler/models.rs b/crates/sqlbuild-analysis/src/compiler/models.rs index ca6719b481..673c5a7112 100644 --- a/crates/sqlbuild-analysis/src/compiler/models.rs +++ b/crates/sqlbuild-analysis/src/compiler/models.rs @@ -265,14 +265,16 @@ pub struct SqlTestAssemblyModelPayload { pub assertion_target_model_names: Vec, pub reference_target_model_names: Vec, pub mock_model_names: Vec, - pub has_macro_mocks: bool, } -/// One test's assembled facts, or why Python assembles it. +/// One test's assembled facts, or why its assembly raises. #[derive(Debug, PartialEq)] pub enum SqlTestAssemblyOutcome { Assembled(AssembledSqlTestFacts), - Deferred(SqlTestAssemblyDeferral), + /// Python's decimal context overflows normalizing a case parameter: Python raises + /// `decimal.Overflow` after reporting the facts' diagnostics and building macro-mock queries. + FingerprintOverflow(AssembledSqlTestFacts), + Failed(SqlTestAssemblyFailure), } /// The facts Python's `_assemble_compiled_sql_test` computes for one test. @@ -299,36 +301,11 @@ pub struct SqlTestHelperDiagnostic { pub help: String, } -/// Why one test is assembled by Python instead. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum SqlTestAssemblyDeferral { - /// Macro mocks re-expand every model, which only Python's macro expansion does. - MacroMocks, - /// A model's macro call scan raises in Python or meets text Python classifies by Unicode. - MacroCallScan, - /// The reference scan fails or cannot classify the text as Python does. - ReferenceScan, - /// Non-ASCII text where Python's case folding or Unicode classes would apply. - NonAsciiText, - /// A decimal parameter Python's decimal context would round or clamp. - DecimalContext, - /// A parameter value the JSON encoder cannot encode as Python's does. - UnencodableValue, - /// A parsed CTE tree too deep, or otherwise unfit, to read as Python's `to_dict` walk does. - UnreadableTree, -} - -impl SqlTestAssemblyDeferral { - /// The kind recorded for the deferral. - pub fn as_str(self) -> &'static str { - match self { - Self::MacroMocks => "macro_mocks", - Self::MacroCallScan => "macro_call_scan", - Self::ReferenceScan => "reference_scan", - Self::NonAsciiText => "non_ascii_text", - Self::DecimalContext => "decimal_context", - Self::UnencodableValue => "unencodable_value", - Self::UnreadableTree => "unreadable_tree", - } - } +/// Why one test's assembly raises, as Python's assembly would. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SqlTestAssemblyFailure { + /// The message of the `CompileInputError` Python raises before the test's macro-mock queries. + Input(String), + /// An internal native failure. + Internal(String), } diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs index 05d1594a41..e2d0a816ce 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs @@ -3,10 +3,9 @@ use serde_json::{Value, json}; use crate::compiler::_helpers::sql_tests::extraction::generic_syntax; use crate::compiler::main::sql_test_assembly::assemble_sql_test_batch; use crate::compiler::models::{ - AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyDeferral, SqlTestAssemblyModel, - SqlTestAssemblyModelPayload, SqlTestAssemblyOutcome, SqlTestAssemblyPayload, - SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestHelperDiagnostic, - SqlTestParameterValue, + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, + SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyReference, SqlTestAssemblyTest, + SqlTestCte, SqlTestHelperDiagnostic, SqlTestParameterValue, }; use crate::compiler::tests::helpers::{ chain_helper_reference_case, defined_before, plan_helper_reference_case, @@ -45,6 +44,12 @@ __expected__customer_totals AS (SELECT 1 AS customer_id)\nSELECT 1\n"; /// Python's `build_sql_test_case_fingerprint` of the parameterized case below. const PYTHON_CASE_FINGERPRINT: &str = "8f2adcab557ba4476fcb70de1fc11014acda9f1b597a3ca5c3245afb632e5d0f"; +/// Python's fingerprint of the case with 29 ones, which its decimal context rounds to 28 digits. +const PYTHON_ROUNDED_CASE_FINGERPRINT: &str = + "58f918fcd3fa8df50c2b10a9e8671c1f3b49eb80af3d48b7eb609392e255ccb4"; +/// Python's fingerprint of the case with 29 nines, whose rounding carries into a new digit. +const PYTHON_CARRIED_CASE_FINGERPRINT: &str = + "6da8c843ce65781616436ca7665394b9495efc3881955fbe3b58be610e3faa7a"; #[test] fn given_helper_cte_references_when_planning_then_references_resolve_in_dependency_order() { @@ -509,14 +514,14 @@ fn given_unresolvable_reader_references_when_planning_directly_then_planner_reje } #[test] -fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_defer() { +fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python() { let cte = |name: &str, sql_body: &str| SqlTestCte { name: name.to_owned(), sql_body: sql_body.to_owned(), }; let strings = |names: &[&str]| -> Vec { names.iter().map(|n| (*n).to_owned()).collect() }; - let model_test = |contents: &str, has_macro_mocks: bool| SqlTestAssemblyTest { + let model_test = |contents: &str| SqlTestAssemblyTest { block_name: Some("mock_reads_helper".to_owned()), file_stem: "test_customer_totals".to_owned(), relative_path: "tests/unit/test_customer_totals.sql".to_owned(), @@ -545,7 +550,6 @@ fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_def assertion_target_model_names: Vec::new(), reference_target_model_names: Vec::new(), mock_model_names: strings(&["stg_orders"]), - has_macro_mocks, }), }; let model = |name: &str, macro_deps: &[&str], source: Option<&str>, refs: &[(&str, &str)]| { @@ -592,7 +596,7 @@ fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_def mode: "macro".to_owned(), tested_resource_names: strings(&["dollars"]), }, - ..model_test(MOCK_READS_HELPER_TEST, false) + ..model_test(MOCK_READS_HELPER_TEST) }; let case_test = |decimal_digits: Vec| SqlTestAssemblyTest { relative_path: "tests/unit/test_cases.sql".to_owned(), @@ -624,14 +628,26 @@ fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_def assertion_target_model_names: strings(&["orders"]), reference_target_model_names: Vec::new(), mock_model_names: Vec::new(), - has_macro_mocks: false, }), - ..model_test(MOCK_READS_HELPER_TEST, false) + ..model_test(MOCK_READS_HELPER_TEST) + }; + let rounded_case = |fingerprint: &str| { + SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { + name: "mock_reads_helper [first]".to_owned(), + scope_deps: vec![ + ("model", "orders".to_owned()), + ("model", "customers".to_owned()), + ], + target_model_names: strings(&["orders", "customers"]), + case_fingerprint: Some(fingerprint.to_owned()), + diagnostic_resource_name: "mock_reads_helper".to_owned(), + diagnostics: Vec::new(), + }) }; let test_cases = [ SqlTestAssemblyTestCase { description: "a mock reading a helper that calls a source reports P013 at the call", - batch: batch(vec![model_test(MOCK_READS_HELPER_TEST, false)]), + batch: batch(vec![model_test(MOCK_READS_HELPER_TEST)]), expected_outcomes: vec![SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { name: "mock_reads_helper".to_owned(), scope_deps: vec![("model", "customer_totals".to_owned())], @@ -657,16 +673,38 @@ fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_def })], }, SqlTestAssemblyTestCase { - description: "macro mocks, non-ASCII text and out-of-context decimals defer", + description: "non-ASCII contents and decimals Python's context rounds assemble as Python", batch: batch(vec![ - model_test(MOCK_READS_HELPER_TEST, true), - model_test(&format!("-- caf\u{e9}\n{MOCK_READS_HELPER_TEST}"), false), + model_test(&format!("-- caf\u{e9}\n{MOCK_READS_HELPER_TEST}")), case_test(vec![1; 29]), + case_test(vec![9; 29]), ]), expected_outcomes: vec![ - SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::MacroMocks), - SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::NonAsciiText), - SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::DecimalContext), + SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { + name: "mock_reads_helper".to_owned(), + scope_deps: vec![("model", "customer_totals".to_owned())], + target_model_names: strings(&["customer_totals"]), + case_fingerprint: None, + diagnostic_resource_name: "mock_reads_helper".to_owned(), + diagnostics: vec![SqlTestHelperDiagnostic { + line: 5, + column: 31, + end_line: 5, + end_column: 53, + message: "SQL test mock '__ref__stg_orders' reads helper CTE \ + 'orders_feed', which calls __source(\"raw_orders\"); mocks \ + and fixtures are defined before the models the test runs, so \ + the helper cannot be resolved for them" + .to_owned(), + help: "Read a mock by its CTE name instead, for example FROM \ + __source__raw_orders rather than FROM __source(\"raw_orders\"), \ + defining __source__raw_orders AS (SELECT ...) if the test does \ + not mock it, or write the rows of '__ref__stg_orders' directly." + .to_owned(), + }], + }), + rounded_case(PYTHON_ROUNDED_CASE_FINGERPRINT), + rounded_case(PYTHON_CARRIED_CASE_FINGERPRINT), ], }, SqlTestAssemblyTestCase { diff --git a/crates/sqlbuild-core/src/text/_helpers/case_tables.rs b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs index 07b7f05015..bfb489d898 100644 --- a/crates/sqlbuild-core/src/text/_helpers/case_tables.rs +++ b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs @@ -14,6 +14,23 @@ pub(crate) const UPPER_TABLES: [(&str, &[(u32, &str)]); 3] = [ ("16.0.0", include!("../upper_mappings/unicode_16_0_0.in")), ]; +/// `(unicodedata.unidata_version, keys)` of every character whose `re.IGNORECASE` key is not +/// itself. +pub(crate) const IGNORECASE_KEY_TABLES: [(&str, &[(u32, u32)]); 3] = [ + ( + "15.0.0", + include!("../re_ignorecase_keys/unicode_15_0_0.in"), + ), + ( + "15.1.0", + include!("../re_ignorecase_keys/unicode_15_1_0.in"), + ), + ( + "16.0.0", + include!("../re_ignorecase_keys/unicode_16_0_0.in"), + ), +]; + /// One string with each character replaced by its `mappings` entry, if it has one. pub(crate) fn mapped(mappings: &[(u32, &str)], text: &str) -> String { let mut result: String = String::with_capacity(text.len()); diff --git a/crates/sqlbuild-core/src/text/main/active_python_text.rs b/crates/sqlbuild-core/src/text/main/active_python_text.rs index 220c7dfee5..2c4ffeb20b 100644 --- a/crates/sqlbuild-core/src/text/main/active_python_text.rs +++ b/crates/sqlbuild-core/src/text/main/active_python_text.rs @@ -3,7 +3,7 @@ use std::sync::OnceLock; use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; -use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, UPPER_TABLES}; +use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, IGNORECASE_KEY_TABLES, UPPER_TABLES}; use crate::text::models::{CleandocMargin, PythonText}; static ACTIVE: OnceLock = OnceLock::new(); @@ -21,6 +21,7 @@ pub fn active_python_text() -> PythonText { decimal_ranges: DECIMAL_TABLES[0].1, casefold_mappings: CASEFOLD_TABLES[0].1, upper_mappings: UPPER_TABLES[0].1, + ignorecase_keys: IGNORECASE_KEY_TABLES[0].1, cleandoc_margin: CleandocMargin::Whitespace, minor_version: 12, }) diff --git a/crates/sqlbuild-core/src/text/main/mod.rs b/crates/sqlbuild-core/src/text/main/mod.rs index 0353af27a4..1e3aeb5c64 100644 --- a/crates/sqlbuild-core/src/text/main/mod.rs +++ b/crates/sqlbuild-core/src/text/main/mod.rs @@ -9,6 +9,7 @@ pub mod is_python_word; pub mod python_casefold; pub mod python_cleandoc; pub mod python_decimal_value; +pub mod python_ignorecase_key; pub mod python_strip; pub mod python_text; pub mod python_upper; diff --git a/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs b/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs new file mode 100644 index 0000000000..f5c5a9553e --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs @@ -0,0 +1,18 @@ +//! Python's `re.IGNORECASE` character matching, from a table generated by Python. + +use crate::text::models::PythonText; + +/// The key under which `re.IGNORECASE` matches `character`: a pattern character matches a text +/// character exactly when both keys are equal. +pub fn python_ignorecase_key(python: PythonText, character: char) -> char { + if character.is_ascii() { + return character.to_ascii_lowercase(); + } + let code_point: u32 = u32::from(character); + python + .ignorecase_keys + .binary_search_by_key(&code_point, |(key, _)| *key) + .ok() + .and_then(|index| char::from_u32(python.ignorecase_keys[index].1)) + .unwrap_or(character) +} diff --git a/crates/sqlbuild-core/src/text/main/python_text.rs b/crates/sqlbuild-core/src/text/main/python_text.rs index 8b6c8afdf7..2850a6d757 100644 --- a/crates/sqlbuild-core/src/text/main/python_text.rs +++ b/crates/sqlbuild-core/src/text/main/python_text.rs @@ -1,7 +1,7 @@ //! Select the Python string semantics of one CPython release. use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; -use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, UPPER_TABLES}; +use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, IGNORECASE_KEY_TABLES, UPPER_TABLES}; use crate::text::models::{CleandocMargin, PythonText}; /// The semantics of one Python and its `unicodedata.unidata_version`, or `None` if unknown. @@ -31,7 +31,12 @@ pub fn python_text(python_version: (u8, u8), unicode_version: &str) -> Option, bool); +/// One call site: start, end, name, tree names if scanned and whether typed reference text +/// appears. +pub(crate) type SiteRow = (usize, usize, String, Option>, bool); /// One substitution span: source start and end, output start and end. pub(crate) type SpanRow = (usize, usize, usize, usize); /// One recorded event: its tag and two text fields. diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs index 1d4677fa4a..12ca1c7984 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs @@ -3,36 +3,45 @@ use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_core::panics::main::native_failure::native_failure; use sqlbuild_core::text::main::python_text::python_text; use sqlbuild_render::macro_calls::main::scan_macro_call_sites::scan_macro_call_sites as scan_sites; use sqlbuild_render::macro_calls::main::splice_macro_calls::splice_macro_calls as splice_calls; -use sqlbuild_render::macro_calls::models::ScanDeferral; +use sqlbuild_render::macro_calls::models::{MacroCallScan, MacroScanFailure}; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::_helpers::render::macro_call_memo::MacroCallMemo; use crate::bindings::_helpers::render::macro_call_rows::{SiteRow, SpanRow, site_row, span_row}; use crate::bindings::types::CompilerDetach; -/// Every top-level macro call site of `sql`, or `None` when Python must expand the string. +/// `(call start or None, message)` of the error Python's scan raises. +type FailureRow = (Option, &'static str); + +/// Every complete top-level macro call site of `sql`, and where Python's scan raises, if it does. #[pyfunction] pub(crate) fn scan_macro_call_sites( py: Python<'_>, sql: &str, python_version: (u8, u8), unicode_version: &str, -) -> PyResult>> { - let Some(python) = python_text(python_version, unicode_version) else { - return Ok(None); - }; - let scanned = py +) -> PyResult<(Vec, Option)> { + let python = python_text(python_version, unicode_version).ok_or_else(|| { + compiler_error(native_failure( + MACRO_SCAN_CONTEXT, + "the macro bridge runs on a Python whose text tables are not compiled in", + )) + })?; + let MacroCallScan { sites, failure } = py .compiler_detach(|| Ok(scan_sites(python, sql))) .map_err(compiler_error)?; - match scanned { - Ok(sites) => Ok(Some(sites.into_iter().map(site_row).collect())), - Err(ScanDeferral) => Ok(None), - } + Ok(( + sites.into_iter().map(site_row).collect(), + failure.map(|MacroScanFailure { call_start, error }| (call_start, error.message())), + )) } +const MACRO_SCAN_CONTEXT: &str = "native macro call scan"; + /// Splice rendered macro outputs over their call sites, returning the SQL and substitution spans. #[pyfunction] pub(crate) fn splice_macro_calls( diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs index 8769cb3b70..87cb1ac3a9 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs @@ -5,16 +5,22 @@ use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyModule, PyModuleMethods, PyRes use pyo3::{FromPyObject, PyErr, pyfunction, wrap_pyfunction}; use sqlbuild_analysis::compiler::main::sql_test_assembly::assemble_sql_test_batch; use sqlbuild_analysis::compiler::models::{ - AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, - SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyReference, SqlTestAssemblyTest, - SqlTestCte, SqlTestParameterValue, + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyFailure, SqlTestAssemblyModel, + SqlTestAssemblyModelPayload, SqlTestAssemblyOutcome, SqlTestAssemblyPayload, + SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestParameterValue, }; +use sqlbuild_core::panics::main::native_failure::native_failure; + use crate::bindings::_helpers::boundary::panics::value_error; use crate::bindings::_helpers::sqltext::lexical_syntax::LexicalSyntaxInput; use crate::bindings::types::CompilerDetach; const MACRO_TOKEN: char = '@'; +const INPUT_FAILURE: &str = "input"; +const INTERNAL_FAILURE: &str = "internal"; +const DECIMAL_OVERFLOW: &str = "decimal_overflow"; +const SQL_TEST_ASSEMBLY_CONTEXT: &str = "native SQL test assembly"; /// `(line, column, end line, end column, message, help)` of one P013 diagnostic. type DiagnosticRow = (usize, usize, usize, usize, String, String); @@ -27,8 +33,9 @@ type FactsRow = ( String, Vec, ); -/// The test's facts, or the deferral kind of a test Python assembles. -type AssemblyRow = (Option, Option<&'static str>); +/// The test's facts, and the `(kind, message)` of the error its assembly raises, if any: kind +/// `input` (no facts), `internal` (no facts) or `decimal_overflow` (after the facts). +type AssemblyRow = (Option, Option<(&'static str, String)>); /// The Python assembly request: the project's model inputs and the SQL test inputs. #[derive(FromPyObject)] @@ -41,7 +48,7 @@ struct AssemblyRequest<'py> { lexical_syntax: LexicalSyntaxInput, } -/// Each test input's assembled facts, or the deferral of a test Python must assemble. +/// Each test input's assembled facts, or the error its assembly raises. #[pyfunction] fn assemble_compiled_sql_tests( py: Python<'_>, @@ -120,7 +127,6 @@ fn assembly_test(test: &Bound<'_, PyAny>) -> PyResult { .getattr("reference_target_model_names")? .extract()?, mock_model_names: payload.getattr("mock_model_names")?.extract()?, - has_macro_mocks: payload.getattr("macro_mocks")?.is_truthy()?, }) }; Ok(SqlTestAssemblyTest { @@ -219,7 +225,20 @@ fn parameter_values(items: &Bound<'_, PyAny>) -> PyResult AssemblyRow { match outcome { SqlTestAssemblyOutcome::Assembled(facts) => (Some(facts_row(facts)), None), - SqlTestAssemblyOutcome::Deferred(deferral) => (None, Some(deferral.as_str())), + SqlTestAssemblyOutcome::FingerprintOverflow(facts) => ( + Some(facts_row(facts)), + Some((DECIMAL_OVERFLOW, String::new())), + ), + SqlTestAssemblyOutcome::Failed(SqlTestAssemblyFailure::Input(message)) => { + (None, Some((INPUT_FAILURE, message))) + } + SqlTestAssemblyOutcome::Failed(SqlTestAssemblyFailure::Internal(reason)) => ( + None, + Some(( + INTERNAL_FAILURE, + native_failure(SQL_TEST_ASSEMBLY_CONTEXT, &reason), + )), + ), } } diff --git a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs index 71cef2c882..1cf06b2bf1 100644 --- a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs +++ b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs @@ -1,10 +1,11 @@ -//! Byte offset port of the Python macro call scanner; unsure or rejected text defers to Python. +//! Byte offset port of the Python macro call scanner, with the errors Python's scan raises. use sqlbuild_core::text::main::is_python_alnum::is_python_alnum; +use sqlbuild_core::text::main::is_python_alpha::is_python_alpha; use sqlbuild_core::text::main::is_python_space::is_python_space; use sqlbuild_core::text::models::PythonText; -use crate::macro_calls::models::ScanDeferral; +use crate::macro_calls::models::ScanError; const TYPED_REFERENCE_NAMES: [&str; 3] = ["__ref", "__source", "__seed"]; @@ -16,26 +17,51 @@ pub(crate) struct ScannedCall { pub(crate) name: String, } -/// Return every top-level call of `text` in order. +/// Where Python's scan of the text raises: in the call starting at a byte offset, or between +/// calls. +pub(crate) struct ScannedFailure { + pub(crate) call_start: Option, + pub(crate) error: ScanError, +} + +/// Every top-level call of `text` in order, up to the first error Python's scan raises. pub(crate) fn top_level_calls( python: PythonText, text: &str, -) -> Result, ScanDeferral> { +) -> (Vec, Option) { let mut calls: Vec = Vec::new(); let mut cursor = 0; while cursor < text.len() { - let Some(start) = next_macro_start(python, text, cursor)? else { - break; + let start = match next_macro_start(python, text, cursor) { + Ok(Some(start)) => start, + Ok(None) => break, + Err(error) => { + let failure = ScannedFailure { + call_start: None, + error, + }; + return (calls, Some(failure)); + } }; - let call = call_at(python, text, start)?; - cursor = call.close + 1; - calls.push(call); + match call_at(python, text, start) { + Ok(call) => { + cursor = call.close + 1; + calls.push(call); + } + Err(error) => { + let failure = ScannedFailure { + call_start: Some(start), + error, + }; + return (calls, Some(failure)); + } + } } - Ok(calls) + (calls, None) } /// Names of every call nested in `args` in source order, in one pass without recursion. -pub(crate) fn nested_names(python: PythonText, args: &str) -> Result, ScanDeferral> { +pub(crate) fn nested_names(python: PythonText, args: &str) -> Result, ScanError> { let bytes: &[u8] = args.as_bytes(); let mut names: Vec = Vec::new(); let mut open_call_depths: Vec = Vec::new(); @@ -47,7 +73,7 @@ pub(crate) fn nested_names(python: PythonText, args: &str) -> Result b'$' => index = dollar_quote_end(bytes, index)?.unwrap_or(index + 1), b'-' if bytes.get(index + 1) == Some(&b'-') => index = line_comment_end(bytes, index), b'/' if bytes.get(index + 1) == Some(&b'*') => index = block_comment_end(bytes, index)?, - b'@' if macro_call_starts_at(python, args, index)? => { + b'@' if macro_call_starts_at(python, args, index) => { let (name, open) = call_header(python, args, index)?; names.push(name); depth += 1; @@ -62,7 +88,7 @@ pub(crate) fn nested_names(python: PythonText, args: &str) -> Result if open_call_depths.last() == Some(&depth) { let _ = open_call_depths.pop(); } - depth = depth.checked_sub(1).ok_or(ScanDeferral)?; + depth = depth.checked_sub(1).ok_or(ScanError::UnclosedParenthesis)?; index += 1; } _ => index += 1, @@ -71,7 +97,7 @@ pub(crate) fn nested_names(python: PythonText, args: &str) -> Result if open_call_depths.is_empty() { Ok(names) } else { - Err(ScanDeferral) + Err(ScanError::UnclosedParenthesis) } } @@ -80,7 +106,7 @@ pub(crate) fn mentions_typed_reference(args: &str) -> bool { TYPED_REFERENCE_NAMES.iter().any(|name| args.contains(name)) } -fn call_at(python: PythonText, text: &str, start: usize) -> Result { +fn call_at(python: PythonText, text: &str, start: usize) -> Result { let (name, open) = call_header(python, text, start)?; let close = matching_paren(text.as_bytes(), open)?; Ok(ScannedCall { @@ -91,15 +117,11 @@ fn call_at(python: PythonText, text: &str, start: usize) -> Result Result<(String, usize), ScanDeferral> { +fn call_header(python: PythonText, text: &str, start: usize) -> Result<(String, usize), ScanError> { let name_end = identifier_end(python, text, start + 1); let open = skip_whitespace(text, name_end); if text.as_bytes().get(open) != Some(&b'(') { - return Err(ScanDeferral); + return Err(ScanError::MissingParenthesis); } Ok((text[start + 1..name_end].to_owned(), open)) } @@ -108,7 +130,7 @@ fn next_macro_start( python: PythonText, text: &str, from: usize, -) -> Result, ScanDeferral> { +) -> Result, ScanError> { let bytes = text.as_bytes(); let mut index = from; while index < bytes.len() { @@ -118,7 +140,7 @@ fn next_macro_start( b'-' if bytes.get(index + 1) == Some(&b'-') => index = line_comment_end(bytes, index), b'/' if bytes.get(index + 1) == Some(&b'*') => index = block_comment_end(bytes, index)?, b'@' => { - if macro_call_starts_at(python, text, index)? { + if macro_call_starts_at(python, text, index) { return Ok(Some(index)); } index += 1; @@ -129,24 +151,21 @@ fn next_macro_start( Ok(None) } -fn macro_call_starts_at(python: PythonText, text: &str, at: usize) -> Result { +fn macro_call_starts_at(python: PythonText, text: &str, at: usize) -> bool { let Some(first) = char_at(text, at + 1) else { - return Ok(false); + return false; }; - if !first.is_ascii() { - return Err(ScanDeferral); - } - if !(first.is_ascii_alphabetic() || first == '_') { - return Ok(false); + if !(first == '_' || is_python_alpha(python, first)) { + return false; } - let mut cursor = at + 2; + let mut cursor = at + 1 + first.len_utf8(); while let Some(character) = char_at(text, cursor) { if !is_identifier_continue(python, character) { break; } cursor = skip_whitespace(text, cursor + character.len_utf8()); } - Ok(text.as_bytes().get(cursor) == Some(&b'(')) + text.as_bytes().get(cursor) == Some(&b'(') } fn identifier_end(python: PythonText, text: &str, from: usize) -> usize { @@ -179,7 +198,7 @@ fn char_at(text: &str, index: usize) -> Option { text.get(index..).and_then(|rest| rest.chars().next()) } -fn matching_paren(bytes: &[u8], open: usize) -> Result { +fn matching_paren(bytes: &[u8], open: usize) -> Result { let mut depth = 1usize; let mut index = open + 1; while index < bytes.len() { @@ -213,10 +232,10 @@ fn matching_paren(bytes: &[u8], open: usize) -> Result { } index += 1; } - Err(ScanDeferral) + Err(ScanError::UnclosedParenthesis) } -fn quote_end(bytes: &[u8], start: usize) -> Result { +fn quote_end(bytes: &[u8], start: usize) -> Result { let quote = bytes[start]; let doubled_escapes = quote == b'\'' || quote == b'"'; let mut index = start + 1; @@ -224,7 +243,7 @@ fn quote_end(bytes: &[u8], start: usize) -> Result { let offset = bytes[index.min(bytes.len())..] .iter() .position(|byte| *byte == quote) - .ok_or(ScanDeferral)?; + .ok_or(ScanError::UnclosedQuote)?; index += offset; if doubled_escapes && bytes.get(index + 1) == Some("e) { index += 2; @@ -234,7 +253,7 @@ fn quote_end(bytes: &[u8], start: usize) -> Result { } } -fn dollar_quote_end(bytes: &[u8], start: usize) -> Result, ScanDeferral> { +fn dollar_quote_end(bytes: &[u8], start: usize) -> Result, ScanError> { if start .checked_sub(1) .is_some_and(|previous| continues_dollar_word(bytes[previous])) @@ -254,7 +273,7 @@ fn dollar_quote_end(bytes: &[u8], start: usize) -> Result, ScanDef .windows(delimiter.len()) .position(|window| window == delimiter) .map(|offset| Some(tag_end + 1 + offset + delimiter.len())) - .ok_or(ScanDeferral) + .ok_or(ScanError::UnclosedQuote) } fn continues_dollar_word(byte: u8) -> bool { @@ -268,10 +287,10 @@ fn line_comment_end(bytes: &[u8], start: usize) -> usize { .map_or(bytes.len(), |offset| start + offset + 1) } -fn block_comment_end(bytes: &[u8], start: usize) -> Result { +fn block_comment_end(bytes: &[u8], start: usize) -> Result { bytes[start + 2..] .windows(2) .position(|pair| pair == b"*/") .map(|offset| start + 2 + offset + 2) - .ok_or(ScanDeferral) + .ok_or(ScanError::UnclosedComment) } diff --git a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs index 47598cc9dd..d37a520b16 100644 --- a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs @@ -4,39 +4,49 @@ use sqlbuild_core::text::models::PythonText; use crate::macro_calls::_helpers::offsets::char_offsets; use crate::macro_calls::_helpers::scanner::{ - mentions_typed_reference, nested_names, top_level_calls, + ScannedFailure, mentions_typed_reference, nested_names, top_level_calls, }; -use crate::macro_calls::models::{MacroCallSite, ScanDeferral}; +use crate::macro_calls::models::{MacroCallScan, MacroCallSite, MacroScanFailure}; -/// Every top-level call site in order under `python`'s character classes, or a deferral. -pub fn scan_macro_call_sites( - python: PythonText, - sql: &str, -) -> Result, ScanDeferral> { - let calls = top_level_calls(python, sql)?; - let boundaries: Vec = calls +/// Every complete top-level call site in order under `python`'s character classes, and where +/// Python's scan raises, if it does. +pub fn scan_macro_call_sites(python: PythonText, sql: &str) -> MacroCallScan { + let (calls, failure) = top_level_calls(python, sql); + let mut boundaries: Vec = calls .iter() .flat_map(|call| [call.start, call.close + 1]) .collect(); + let failed_call: Option = failure.as_ref().and_then(|failure| failure.call_start); + boundaries.extend(failed_call); let offsets = char_offsets(sql, &boundaries); - calls + let sites: Vec = calls .into_iter() .zip(offsets.as_chunks::<2>().0) .map(|(call, offsets)| { let args = &sql[call.open + 1..call.close]; - let mut tree_names: Vec = vec![call.name.clone()]; - for name in nested_names(python, args)? { - if !tree_names.contains(&name) { - tree_names.push(name); + let tree_names: Option> = nested_names(python, args).ok().map(|nested| { + let mut tree_names: Vec = vec![call.name.clone()]; + for name in nested { + if !tree_names.contains(&name) { + tree_names.push(name); + } } - } - Ok(MacroCallSite { + tree_names + }); + MacroCallSite { start: offsets[0], end: offsets[1], name: call.name, tree_names, typed_reference_text: mentions_typed_reference(args), - }) + } }) - .collect() + .collect(); + MacroCallScan { + sites, + failure: failure.map(|ScannedFailure { call_start, error }| MacroScanFailure { + call_start: call_start.and(offsets.last().copied()), + error, + }), + } } diff --git a/crates/sqlbuild-render/src/macro_calls/models.rs b/crates/sqlbuild-render/src/macro_calls/models.rs index d2d5ada473..0f8bfa9933 100644 --- a/crates/sqlbuild-render/src/macro_calls/models.rs +++ b/crates/sqlbuild-render/src/macro_calls/models.rs @@ -18,15 +18,49 @@ pub struct MacroCallSite { pub end: usize, /// The called macro's name. pub name: String, - /// Every macro name in the call, the top-level name first, then nested calls once each. - pub tree_names: Vec, + /// Every macro name in the call, the top-level name first, then nested calls once each; + /// None where a nested call is not one Python's scan completes. + pub tree_names: Option>, /// The arguments mention `__ref`, `__source` or `__seed`, so typed references may be passed. pub typed_reference_text: bool, } -/// The scan met text Python reports or classifies differently; Python must expand the string. +/// An error Python's macro call scan raises. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ScanDeferral; +pub enum ScanError { + UnclosedQuote, + UnclosedComment, + UnclosedParenthesis, + /// A call start whose name is not followed by its parenthesis. + MissingParenthesis, +} + +impl ScanError { + /// The message of the `CompileInputError` Python's scan raises. + pub fn message(self) -> &'static str { + match self { + Self::UnclosedQuote => "Macro expansion contains an unclosed quoted string", + Self::UnclosedComment => "Macro expansion contains an unclosed block comment", + Self::UnclosedParenthesis => "Macro expansion contains an unclosed parenthesis", + Self::MissingParenthesis => "expected opening parenthesis", + } + } +} + +/// Where a scan stops: inside the call at a code point offset, or between calls. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MacroScanFailure { + /// Offset of the `@` of the call Python's evaluation raises in, if any. + pub call_start: Option, + pub error: ScanError, +} + +/// A string's complete top-level call sites in order, and where the scan stopped, if it did. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MacroCallScan { + pub sites: Vec, + pub failure: Option, +} /// One consumer-independent fact a macro call produced, replayed for every consumer in order. #[derive(Clone, Debug, PartialEq, Eq, Hash)] diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs index 0ee44440e9..9d03816e34 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs @@ -1,4 +1,5 @@ use crate::macro_calls::main::scan_macro_call_sites::scan_macro_call_sites; +use crate::macro_calls::models::ScanError; use crate::macro_calls::tests::helpers::python_312; use std::time::Instant; @@ -12,119 +13,161 @@ fn given_authored_sql_when_scanning_macro_calls_then_sites_match_python_expansio ScanMacroCallSitesTestCase { description: "one call", sql: "SELECT @a(1) FROM t", - expected_sites: Some(&[(7, 12, "a", &["a"], false)]), + expected_sites: &[(7, 12, "a", Some(&["a"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "nested calls belong to the enclosing site", sql: "@outer(@inner(@leaf()), '@quoted()', @inner(2)) y", - expected_sites: Some(&[(0, 47, "outer", &["outer", "inner", "leaf"], false)]), + expected_sites: &[(0, 47, "outer", Some(&["outer", "inner", "leaf"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "quoted text and comments hide calls", sql: "'@a(1)' -- @b(2)\n/* @c(3) */ \"@d()\" `@e()` $$@f()$$ @g()", - expected_sites: Some(&[(52, 56, "g", &["g"], false)]), + expected_sites: &[(52, 56, "g", Some(&["g"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "a one-letter name followed by whitespace is not a call", sql: "@a (1)", - expected_sites: Some(&[]), + expected_sites: &[], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "a longer name may be separated from its parenthesis", sql: "@ab (1)", - expected_sites: Some(&[(0, 7, "ab", &["ab"], false)]), + expected_sites: &[(0, 7, "ab", Some(&["ab"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { - description: "whitespace inside a name defers to Python's error", + description: "whitespace inside a name fails in that call, as Python's evaluation does", sql: "@ab c(1)", - expected_sites: None, + expected_sites: &[], + expected_failure: Some((Some(0), ScanError::MissingParenthesis)), }, ScanMacroCallSitesTestCase { - description: "an unclosed quote after a call defers", + description: "an unclosed quote after a call fails after it", sql: "@a(1) 'oops", - expected_sites: None, + expected_sites: &[(0, 5, "a", Some(&["a"]), false)], + expected_failure: Some((None, ScanError::UnclosedQuote)), }, ScanMacroCallSitesTestCase { - description: "an unclosed call defers", + description: "an unclosed call fails in that call", sql: "@a(1", - expected_sites: None, + expected_sites: &[], + expected_failure: Some((Some(0), ScanError::UnclosedParenthesis)), }, ScanMacroCallSitesTestCase { description: "offsets count code points", sql: "SELECT 'é', @a(1)", - expected_sites: Some(&[(12, 17, "a", &["a"], false)]), + expected_sites: &[(12, 17, "a", Some(&["a"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { - description: "a non-ASCII character after @ defers", + description: "a non-ASCII letter after @ starts a call as Python's isalpha says", sql: "@é(1)", - expected_sites: None, + expected_sites: &[(0, 5, "é", Some(&["é"]), false)], + expected_failure: None, + }, + ScanMacroCallSitesTestCase { + description: "a non-letter non-ASCII character after @ is not a call", + sql: "@€(1) @m()", + expected_sites: &[(6, 10, "m", Some(&["m"]), false)], + expected_failure: None, + }, + ScanMacroCallSitesTestCase { + description: "an unclosed block comment between calls fails there", + sql: "@a() /* @b()", + expected_sites: &[(0, 4, "a", Some(&["a"]), false)], + expected_failure: Some((None, ScanError::UnclosedComment)), + }, + ScanMacroCallSitesTestCase { + description: "an unclosed dollar quote inside a call fails in that call", + sql: "x @a() é @b($tag$ )", + expected_sites: &[(2, 6, "a", Some(&["a"]), false)], + expected_failure: Some((Some(9), ScanError::UnclosedQuote)), }, ScanMacroCallSitesTestCase { description: "typed reference text is flagged", sql: "@m(__ref('x')) @n('__seed') @o(1)", - expected_sites: Some(&[ - (0, 14, "m", &["m"], true), - (15, 27, "n", &["n"], true), - (28, 33, "o", &["o"], false), - ]), + expected_sites: &[ + (0, 14, "m", Some(&["m"]), true), + (15, 27, "n", Some(&["n"]), true), + (28, 33, "o", Some(&["o"]), false), + ], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "a dollar sign continuing a word does not open a quote", sql: "a$$ @m()", - expected_sites: Some(&[(4, 8, "m", &["m"], false)]), + expected_sites: &[(4, 8, "m", Some(&["m"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "interpolation markers are not calls", sql: "x @@var @m()", - expected_sites: Some(&[(8, 12, "m", &["m"], false)]), + expected_sites: &[(8, 12, "m", Some(&["m"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "nested names follow source order across siblings and plain parentheses", sql: "@a((@b(@c(1)), (@d()) ), \"@x()\", @c())", - expected_sites: Some(&[(0, 38, "a", &["a", "b", "c", "d"], false)]), + expected_sites: &[(0, 38, "a", Some(&["a", "b", "c", "d"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "a quoted parenthesis inside a nested call does not close it", sql: "@a(@b(')'))", - expected_sites: Some(&[(0, 11, "a", &["a", "b"], false)]), + expected_sites: &[(0, 11, "a", Some(&["a", "b"]), false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { - description: "a nested name without its parenthesis defers", + description: "a nested name without its parenthesis leaves the tree unscanned", sql: "@a(@bb c(1))", - expected_sites: None, + expected_sites: &[(0, 12, "a", None, false)], + expected_failure: None, }, ScanMacroCallSitesTestCase { description: "parentheses inside quoted arguments do not close the call", sql: "@m(')', \"(\", `)`) z", - expected_sites: Some(&[(0, 17, "m", &["m"], false)]), + expected_sites: &[(0, 17, "m", Some(&["m"]), false)], + expected_failure: None, }, ]; for test_case in test_cases { - let actual: Option> = scan_macro_call_sites(python_312(), test_case.sql) - .ok() - .map(|sites| { - sites - .into_iter() - .map(|site| { - let names: Vec<&'static str> = site - .tree_names - .iter() - .map(|name| &*Box::leak(name.clone().into_boxed_str())) - .collect(); - ( - site.start, - site.end, - &*Box::leak(site.name.into_boxed_str()), - &*Box::leak(names.into_boxed_slice()), - site.typed_reference_text, - ) - }) - .collect() - }); + let scan = scan_macro_call_sites(python_312(), test_case.sql); + let actual: Vec = scan + .sites + .into_iter() + .map(|site| { + let names: Option<&'static [&'static str]> = site.tree_names.map(|names| { + let names: Vec<&'static str> = names + .into_iter() + .map(|name| &*Box::leak(name.into_boxed_str())) + .collect(); + &*Box::leak(names.into_boxed_slice()) + }); + ( + site.start, + site.end, + &*Box::leak(site.name.into_boxed_str()), + names, + site.typed_reference_text, + ) + }) + .collect(); assert_eq!( - actual, - test_case.expected_sites.map(<[ExpectedSite]>::to_vec), + ( + actual, + scan.failure + .map(|failure| (failure.call_start, failure.error)) + ), + ( + test_case.expected_sites.to_vec(), + test_case.expected_failure + ), "{}", test_case.description ); @@ -162,7 +205,7 @@ fn given_deeply_nested_calls_when_scanning_then_time_is_linear_and_nothing_overf ); let started = Instant::now(); - let sites = scan_macro_call_sites(python_312(), &sql).expect("scanned"); + let scan = scan_macro_call_sites(python_312(), &sql); assert!( started.elapsed().as_secs_f64() < test_case.expected_max_seconds, @@ -170,11 +213,23 @@ fn given_deeply_nested_calls_when_scanning_then_time_is_linear_and_nothing_overf test_case.description ); assert_eq!( - sites - .iter() - .map(|site| site.tree_names.clone()) - .collect::>(), - vec![test_case.expected_tree_names.to_vec()], + ( + scan.sites + .iter() + .map(|site| site.tree_names.clone()) + .collect::>(), + scan.failure, + ), + ( + vec![Some( + test_case + .expected_tree_names + .iter() + .map(|name| (*name).to_owned()) + .collect() + )], + None + ), "{}", test_case.description ); diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs index b58c34f5a4..b782de67f7 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs @@ -1,5 +1,14 @@ -/// One expected call site: start, end, name, tree names and whether typed reference text appears. -pub(crate) type ExpectedSite = (usize, usize, &'static str, &'static [&'static str], bool); +use crate::macro_calls::models::ScanError; + +/// One expected call site: start, end, name, tree names if scanned and whether typed reference +/// text appears. +pub(crate) type ExpectedSite = ( + usize, + usize, + &'static str, + Option<&'static [&'static str]>, + bool, +); /// One expected splice: the rendered SQL and each span's source and output offsets. pub(crate) type ExpectedSplice = (&'static str, &'static [(usize, usize, usize, usize)]); @@ -7,7 +16,8 @@ pub(crate) type ExpectedSplice = (&'static str, &'static [(usize, usize, usize, pub(crate) struct ScanMacroCallSitesTestCase { pub(crate) description: &'static str, pub(crate) sql: &'static str, - pub(crate) expected_sites: Option<&'static [ExpectedSite]>, + pub(crate) expected_sites: &'static [ExpectedSite], + pub(crate) expected_failure: Option<(Option, ScanError)>, } pub(crate) struct SpliceMacroCallsTestCase { diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 5452d4a487..482b67a1c8 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -20,13 +20,6 @@ site = "project_assembly" kind = "unsupported_dialect" counts = { failure = 1 } -[[entry]] -engine = "native" -stage = "analysis" -site = "sql_test_assembly" -kind = "macro_mocks" -counts = { seed = 29 } - [[entry]] engine = "native" stage = "macro_calls" @@ -34,13 +27,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native" -stage = "macro_calls" -site = "macro_calls.resolution" -kind = "deferred" -counts = { failure = 4, seed = 3 } - [[entry]] engine = "native" stage = "project_assembly" @@ -51,8 +37,8 @@ counts = { failure = 4 } [[entry]] engine = "native" stage = "sql_test_glue" -site = "sql_test_glue.assembly" -kind = "deferred" +site = "sql_test_glue.macro_mock_queries" +kind = "extension_callback" counts = { seed = 29 } [[entry]] @@ -69,13 +55,6 @@ site = "project_assembly" kind = "unsupported_dialect" counts = { failure = 1 } -[[entry]] -engine = "native-preview" -stage = "analysis" -site = "sql_test_assembly" -kind = "macro_mocks" -counts = { seed = 29 } - [[entry]] engine = "native-preview" stage = "macro_calls" @@ -83,13 +62,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "macro_calls" -site = "macro_calls.resolution" -kind = "deferred" -counts = { failure = 4, seed = 3 } - [[entry]] engine = "native-preview" stage = "project_assembly" @@ -100,8 +72,8 @@ counts = { failure = 4 } [[entry]] engine = "native-preview" stage = "sql_test_glue" -site = "sql_test_glue.assembly" -kind = "deferred" +site = "sql_test_glue.macro_mock_queries" +kind = "extension_callback" counts = { seed = 29 } [[entry]] diff --git a/scripts/python_char_table/_helpers/table.py b/scripts/python_char_table/_helpers/table.py index 2400356677..d7608e657d 100644 --- a/scripts/python_char_table/_helpers/table.py +++ b/scripts/python_char_table/_helpers/table.py @@ -2,10 +2,13 @@ from __future__ import annotations +import importlib import unicodedata from pathlib import Path +from typing import Any from scripts.python_char_table.constants import ( + IGNORECASE_KEY_TABLE, MAX_CODE_POINT, SURROGATE_RANGE, TABLE_FILE_PREFIX, @@ -59,6 +62,48 @@ def render_mapping(*, method: str, mappings: list[tuple[int, str]]) -> str: return "\n".join(lines) + "\n" +def ignorecase_key_path() -> Path: + """Return the Rust file holding `re.IGNORECASE` match keys for this Unicode version.""" + + version: str = unicodedata.unidata_version.replace(".", "_") + return TABLE_ROOT / IGNORECASE_KEY_TABLE / f"{TABLE_FILE_PREFIX}{version}{TABLE_FILE_SUFFIX}" + + +def ignorecase_keys() -> list[tuple[int, int]]: + """Return every character whose `re.IGNORECASE` match key is not itself, with that key. + + A pattern character matches a text character under `re.IGNORECASE` exactly when both have + the same key: the smallest of the character's `_sre` lower case and its `re._casefix` + equivalents. + """ + + sre: Any = importlib.import_module("_sre") + extra: dict[int, tuple[int, ...]] = importlib.import_module("re._casefix")._EXTRA_CASES # noqa: SLF001 + keys: list[tuple[int, int]] = [] + for code_point in range(MAX_CODE_POINT + 1): + if code_point in SURROGATE_RANGE: + continue + lower: int = sre.unicode_tolower(code_point) + key: int = min((lower, *extra.get(lower, ()))) + if key != code_point: + keys.append((code_point, key)) + return keys + + +def render_ignorecase_keys(keys: list[tuple[int, int]]) -> str: + """Render the Rust expression of the `re.IGNORECASE` key table `case_tables.rs` includes.""" + + lines: list[str] = [ + "// Generated by `scripts/generate_python_char_table.py`; do not edit.", + "// Every character whose `re.IGNORECASE` match key is not itself in Unicode " + f"{unicodedata.unidata_version}, in code point order.", + "&[", + ] + lines.extend(f" (0x{code_point:04X}, 0x{key:04X})," for code_point, key in keys) + lines.append("]") + return "\n".join(lines) + "\n" + + def character_ranges(*, method: str) -> list[tuple[int, int]]: """Return the inclusive code point ranges where the `str` method is true.""" diff --git a/scripts/python_char_table/constants.py b/scripts/python_char_table/constants.py index 4aaa691e43..95bc78d53a 100644 --- a/scripts/python_char_table/constants.py +++ b/scripts/python_char_table/constants.py @@ -8,4 +8,5 @@ MAX_CODE_POINT: int = 0x10FFFF TABLE_METHODS: tuple[str, ...] = ("isalnum", "isalpha", "isdecimal") MAPPING_METHODS: tuple[str, ...] = ("casefold", "upper") +IGNORECASE_KEY_TABLE: str = "re_ignorecase_keys" SURROGATE_RANGE: range = range(0xD800, 0xE000) diff --git a/scripts/python_char_table/main/generate.py b/scripts/python_char_table/main/generate.py index fa40fb8ae9..6587f29c29 100644 --- a/scripts/python_char_table/main/generate.py +++ b/scripts/python_char_table/main/generate.py @@ -9,7 +9,10 @@ from scripts.python_char_table._helpers.table import ( character_mappings, character_ranges, + ignorecase_key_path, + ignorecase_keys, mapping_path, + render_ignorecase_keys, render_mapping, render_table, table_path, @@ -35,4 +38,8 @@ def generate_python_char_table() -> int: encoding="utf-8", ) print(f"wrote {path} (Unicode {unicodedata.unidata_version})", file=sys.stderr) + path = ignorecase_key_path() + path.parent.mkdir(parents=True, exist_ok=True) + _ = path.write_text(render_ignorecase_keys(ignorecase_keys()), encoding="utf-8") + print(f"wrote {path} (Unicode {unicodedata.unidata_version})", file=sys.stderr) return 0 diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 358f96f11e..c78108c03a 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -325,7 +325,7 @@ def interpolate_sql_batch( ]: ... def scan_macro_call_sites( sql: str, python_version: tuple[int, int], unicode_version: str -) -> list[tuple[int, int, str, list[str], bool]] | None: ... +) -> tuple[list[tuple[int, int, str, list[str] | None, bool]], tuple[int | None, str] | None]: ... def splice_macro_calls( sql: str, sites: list[tuple[int, int]], outputs: list[str] ) -> tuple[str, list[tuple[int, int, int, int]]]: ... diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 24d4ba7a47..90cf2c8227 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -53,7 +53,6 @@ audit_scope_deps, function_build_deps, model_build_deps, - sql_test_scope_deps, ) from sqlbuild.compiler.compile._helpers.diagnostics.recovery import complete_semantic_diagnostics from sqlbuild.compiler.compile._helpers.diagnostics.scope import report_scope_index_errors @@ -78,15 +77,6 @@ find_macro_call_names, ) from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data -from sqlbuild.compiler.compile._helpers.sql_tests.helper_ctes import ( - report_mocks_reading_referencing_helpers, -) -from sqlbuild.compiler.compile._helpers.sql_tests.identity import build_sql_test_case_fingerprint -from sqlbuild.compiler.compile._helpers.sql_tests.scope_deps import ( - function_sql_test_scope_deps, - macro_sql_test_scope_deps, - udf_sql_test_scope_deps, -) from sqlbuild.compiler.compile.main._scope_index_with_compile_usages import ( scope_index_with_compile_usages, ) @@ -94,20 +84,15 @@ AnalysisCacheContext, CompileAuditInput, CompiledAudit, - CompiledDirectLogicSqlTestPayload, CompiledFunction, - CompileDirectLogicSqlTestInputPayload, CompiledLineageColumnFact, CompiledModel, - CompiledModelSqlTestPayload, CompiledObjectKey, CompiledProject, CompiledRelationLocation, CompiledSeed, CompiledSource, CompiledSqlScenario, - CompiledSqlTest, - CompiledSqlTestResource, CompileModelInput, CompileModelSqlTestInputPayload, CompileProjectInputs, @@ -131,7 +116,6 @@ CompiledResourceType, DiagnosticPhase, DiagnosticSeverity, - SqlTestMode, ) from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy @@ -175,10 +159,6 @@ class _DynamicContractAnalysisInputs: authoritative_column_types_by_table: dict[str, dict[str, str]] -_COMPACT_BATCH_CACHE_MIN_MODEL_COUNT: int = 256 -_COMPACT_BATCH_ENTRY_REUSE_MIN_PERCENT: int = 10 - - def assemble_compiled_project( *, inputs: CompileProjectInputs, @@ -437,7 +417,7 @@ def assemble_compiled_project( ), sql_tests=assemble_sql_tests_by_engine( inputs=inputs, - assemble_python_test=lambda test_input: _assemble_compiled_sql_test( + macro_mock_queries=lambda test_input: _build_test_model_query_overrides( test_input=test_input, model_inputs=inputs.model_inputs, inputs=inputs ), ), @@ -928,149 +908,6 @@ def _assemble_compiled_audit( ) -def _assemble_compiled_sql_test( - *, - test_input: CompileSqlTestInput, - model_inputs: tuple[CompileModelInput, ...], - inputs: CompileProjectInputs, -) -> CompiledSqlTest: - test_name: str = _resolve_test_name(test_input) - compiled_payload: CompiledModelSqlTestPayload | CompiledDirectLogicSqlTestPayload - scope_deps: tuple[CompiledObjectKey, ...] - target_model_names: tuple[str, ...] = () - if isinstance(test_input.payload, CompileDirectLogicSqlTestInputPayload): - if test_input.payload.mode == SqlTestMode.MACRO: - scope_deps = macro_sql_test_scope_deps( - tested_macro_names=test_input.payload.tested_resource_names, - model_inputs=model_inputs, - ) - elif test_input.payload.mode == SqlTestMode.UDF: - scope_deps = udf_sql_test_scope_deps( - tested_udf_names=test_input.payload.tested_resource_names, - model_inputs=model_inputs, - ) - else: - scope_deps = function_sql_test_scope_deps( - tested_function_names=test_input.payload.tested_resource_names, - ) - compiled_payload = CompiledDirectLogicSqlTestPayload( - mode=test_input.payload.mode, - helper_ctes=test_input.payload.helper_ctes, - actual_cte=test_input.payload.actual_cte, - expected_cte=test_input.payload.expected_cte, - tested_resource_names=test_input.payload.tested_resource_names, - ) - else: - model_payload: CompileModelSqlTestInputPayload = test_input.payload - target_model_names = tuple( - dict.fromkeys( - ( - *model_payload.expected_model_names, - *model_payload.assertion_target_model_names, - *model_payload.reference_target_model_names, - ) - ) - ) - report_mocks_reading_referencing_helpers( - authored_ctes=model_payload.authored_ctes, - reader_ctes=(*model_payload.expected_ctes, *model_payload.assertion_ctes), - model_inputs=model_inputs, - target_model_names=target_model_names, - mock_model_names=model_payload.mock_model_names, - test_file=test_input.test_file, - test_block=test_input.test_block, - syntax=inputs.sql_lexical_syntax, - ) - scope_deps = sql_test_scope_deps(expected_model_names=target_model_names) - compiled_payload = CompiledModelSqlTestPayload( - authored_ctes=model_payload.authored_ctes, - macro_mocks=model_payload.macro_mocks, - model_query_overrides=_build_test_model_query_overrides( - test_input=test_input, - model_inputs=model_inputs, - inputs=inputs, - ), - mock_model_names=model_payload.mock_model_names, - mock_source_names=model_payload.mock_source_names, - mock_seed_names=model_payload.mock_seed_names, - mock_dbt_ref_names=model_payload.mock_dbt_ref_names, - mock_table_function_names=model_payload.mock_table_function_names, - expected_ctes=model_payload.expected_ctes, - expected_model_names=model_payload.expected_model_names, - assertion_ctes=model_payload.assertion_ctes, - assertion_names=model_payload.assertion_names, - ) - tested_resources: tuple[CompiledSqlTestResource, ...] = ( - tuple( - CompiledSqlTestResource(kind=test_input.payload.mode, name=name) - for name in test_input.payload.tested_resource_names - ) - if isinstance(test_input.payload, CompileDirectLogicSqlTestInputPayload) - else () - ) - case_fingerprint: str | None = ( - build_sql_test_case_fingerprint( - source_path=test_input.test_file.relative_path, - block_index=test_input.test_block.test_index, - case_name=test_input.case_name, - parameter_schema=test_input.parameter_schema, - parameter_values=test_input.parameter_values, - expanded_sql=test_input.sql_body, - scope_deps=scope_deps, - tested_resources=tested_resources, - ) - if test_input.case_name is not None - else None - ) - return CompiledSqlTest( - key=CompiledObjectKey(resource_type=CompiledResourceType.SQL_TEST, name=test_name), - scope_deps=scope_deps, - name=test_name, - test_file=test_input.test_file, - test_block=test_input.test_block, - sql_body=test_input.sql_body, - mode=test_input.mode, - payload=compiled_payload, - source_path=test_input.test_file.relative_path, - ownership_root=test_input.test_file.ownership_root, - block_index=test_input.test_block.test_index, - explicit_name=test_input.test_block.name, - parent_name=test_input.parent_name, - case_name=test_input.case_name, - case_index=test_input.case_index, - case_fingerprint=case_fingerprint, - parameter_schema=test_input.parameter_schema, - parameter_values=test_input.parameter_values, - expected_model_names=( - test_input.payload.expected_model_names - if isinstance(test_input.payload, CompileModelSqlTestInputPayload) - else () - ), - assertion_names=( - test_input.payload.assertion_names - if isinstance(test_input.payload, CompileModelSqlTestInputPayload) - else () - ), - assertion_target_model_names=( - test_input.payload.assertion_target_model_names - if isinstance(test_input.payload, CompileModelSqlTestInputPayload) - else () - ), - read_helper_names=( - test_input.payload.read_helper_names - if isinstance(test_input.payload, CompileModelSqlTestInputPayload) - else () - ), - reference_target_model_names=( - test_input.payload.reference_target_model_names - if isinstance(test_input.payload, CompileModelSqlTestInputPayload) - else () - ), - target_model_names=target_model_names, - tested_resources=tested_resources, - ) - - def _build_test_model_query_overrides( *, test_input: CompileSqlTestInput, @@ -1157,10 +994,3 @@ def _resolve_audit_name(audit_input: CompileAuditInput) -> str: if audit_input.audit_block.name is not None: return audit_input.audit_block.name return audit_input.audit_file.file_path.stem - - -def _resolve_test_name(test_input: CompileSqlTestInput) -> str: - parent_name: str = test_input.test_block.name or test_input.test_file.file_path.stem - if test_input.case_name is not None: - return f"{parent_name} [{test_input.case_name}]" - return parent_name diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index 739dece3ea..fe2ffcb39a 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -6,7 +6,6 @@ from sqlbuild.compiler.compile._helpers.render.macros import ( _evaluate_macro_call, - _expand_resolved_sql_macros, _expansion_declarations, _record_macro_declaration_usage, _record_macro_use, @@ -30,7 +29,12 @@ GENERATED_SQL_EVENT, MACRO_USE_EVENT, ) -from sqlbuild.compiler.macro_bridge.models import MacroCallClass, MacroCallSite +from sqlbuild.compiler.macro_bridge.models import ( + MacroCallClass, + MacroCallScan, + MacroCallSite, + MacroScanFailure, +) from sqlbuild.compiler.macro_bridge.types import MacroCallEvent, MacroCallRecord from sqlbuild.compiler.scopes.models import DeclarationIdentity from sqlbuild.compiler.scopes.types import DeclarationKind @@ -45,35 +49,53 @@ def expand_bridged_sql_macros( state: MacroExpansionState, bridge: MacroBridge, ) -> tuple[str, tuple[ExpansionSpan, ...]]: - """Expand one string, replaying memoized calls and running the rest in Python in order.""" + """Expand one string, replaying memoized calls and running the rest in Python in order. + + Where a call's macro tree does not resolve, or the scan raises, no call of the string touches + the memo: each is evaluated in order, so errors and side effects keep the plain expansion's + order. + """ if MACRO_TOKEN not in sql: return sql, () declarations: DeclarationResolutionContext | None = _expansion_declarations( state=state, consumer_path=consumer_path ) - sites: tuple[MacroCallSite, ...] | None = bridge.scan(sql) - call_classes: list[MacroCallClass] = [] - for site in sites or (): - call_class: MacroCallClass | None = bridge.call_class( - site=site, - declarations=declarations, - loaded_macros=state.loaded_macros, - macro_context=state.macro_context, - ) - if call_class is None: - report_native_fallback(site=NativeFallbackSite.MACRO_CALL_RESOLUTION) - sites = None - break - call_classes.append(call_class) - if sites is None: - return _expand_resolved_sql_macros( - sql=sql, - consumer_path=consumer_path, - state=state, - declarations=declarations, - stack=(), - ) + scan: MacroCallScan = bridge.scan(sql) + sites: tuple[MacroCallSite, ...] = scan.sites + call_classes: list[MacroCallClass | None] = ( + [] + if scan.failure is not None + else [ + bridge.call_class( + site=site, + declarations=declarations, + loaded_macros=state.loaded_macros, + macro_context=state.macro_context, + ) + for site in sites + ] + ) + if scan.failure is not None or None in call_classes: + unmemoised: list[str] = [ + _evaluated_call_output( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=declarations, + site=site, + ) + for site in sites + ] + if scan.failure is not None: + _raise_scan_failure( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=declarations, + failure=scan.failure, + ) + return bridge.splice(sql=sql, sites=sites, outputs=unmemoised) outputs: list[str] = [ _bridged_call_output( sql=sql, @@ -85,6 +107,7 @@ def expand_bridged_sql_macros( call_class=call_class, ) for site, call_class in zip(sites, call_classes, strict=True) + if call_class is not None ] return bridge.splice(sql=sql, sites=sites, outputs=outputs) @@ -100,7 +123,7 @@ def _bridged_call_output( # noqa: PLR0913 call_class: MacroCallClass, ) -> str: facts: MacroExpansionFacts = state.facts - if state.macro_overrides.keys() & set(site.tree_names): + if state.macro_overrides.keys() & set(site.tree_names or ()): return _mocked_call_output(sql=sql, consumer_path=consumer_path, state=state, site=site) call_text: str = sql[site.start : site.end] prior_relations: tuple[SqlResourceRef, ...] | None = ( @@ -161,12 +184,57 @@ def _bridged_call_output( # noqa: PLR0913 return facts.render_relation_placeholders(macro_result) +def _raise_scan_failure( + *, + sql: str, + consumer_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, + failure: MacroScanFailure, +) -> None: + """Raise where Python's scan raises: from the failing call's evaluation, or between calls.""" + + if failure.call_start is None: + raise CompileInputError(failure.message) + _ = _evaluate_macro_call( + sql=sql, + call_start_index=failure.call_start, + file_path=consumer_path, + state=state, + declarations=declarations, + stack=(), + top_level=True, + ) + raise NativeStageMismatchError( + f"Native macro call scan of '{consumer_path}' found no complete call at " + f"{failure.call_start}; Python evaluated one" + ) + + def _mocked_call_output( *, sql: str, consumer_path: Path, state: MacroExpansionState, site: MacroCallSite ) -> str: """Run a call whose tree a test mocks; its output differs from the shared memo's, so skip it.""" report_native_fallback(site=NativeFallbackSite.MACRO_CALL_MOCKED, kind="test_mock") + return _evaluated_call_output( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=_expansion_declarations(state=state, consumer_path=consumer_path), + site=site, + ) + + +def _evaluated_call_output( + *, + sql: str, + consumer_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, + site: MacroCallSite, +) -> str: + """Evaluate one natively scanned call in Python without the memo.""" macro_result: object next_index: int @@ -175,7 +243,7 @@ def _mocked_call_output( call_start_index=site.start, file_path=consumer_path, state=state, - declarations=_expansion_declarations(state=state, consumer_path=consumer_path), + declarations=declarations, stack=(), top_level=True, ) diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py index a154f8afce..554011fb02 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py @@ -1,17 +1,28 @@ -"""Assemble compiled SQL tests natively, with Python answering native deferrals.""" +"""Assemble every SQL test natively; macro-mocked tests' model queries run the user's macros.""" from __future__ import annotations +import decimal from collections.abc import Callable +from dataclasses import replace +import sqlbuild._native as _native from sqlbuild.compiler.compile._helpers.diagnostics.collector import report_compile_diagnostic +from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( + CompiledModelSqlTestPayload, CompiledSqlTest, + CompileModelSqlTestInputPayload, CompileProjectInputs, CompileSqlTestInput, ) from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.sql_test_glue.constants import ( + SQL_TEST_DECIMAL_OVERFLOW, + SQL_TEST_EXTENSION_CALLBACK, + SQL_TEST_INPUT_FAILURE, +) from sqlbuild.compiler.sql_test_glue.main._assemble_native_sql_tests import ( assemble_native_sql_tests, ) @@ -21,18 +32,37 @@ def assemble_sql_tests_by_engine( *, inputs: CompileProjectInputs, - assemble_python_test: Callable[[CompileSqlTestInput], CompiledSqlTest], + macro_mock_queries: Callable[[CompileSqlTestInput], dict[str, str]], ) -> tuple[CompiledSqlTest, ...]: - """Assemble every SQL test natively, or each test in Python where native defers.""" + """Assemble every SQL test natively; a test mocking macros re-expands each model's query + with the user's macros, and errors raise in Python's assembly order.""" - native: tuple[NativeSqlTestAssembly | None, ...] = assemble_native_sql_tests(inputs=inputs) + native: tuple[NativeSqlTestAssembly, ...] = assemble_native_sql_tests(inputs=inputs) tests: list[CompiledSqlTest] = [] for test_input, assembly in zip(inputs.test_inputs, native, strict=True): - if assembly is None: - report_native_fallback(site=NativeFallbackSite.SQL_TEST_ASSEMBLY) - tests.append(assemble_python_test(test_input)) - continue + if assembly.test is None: + message: str = assembly.failure.message if assembly.failure is not None else "" + if assembly.failure is not None and assembly.failure.kind == SQL_TEST_INPUT_FAILURE: + raise CompileInputError(message) + raise _native.NativeCompilerError(message) for key, diagnostic in assembly.diagnostics: report_compile_diagnostic(key=key, diagnostic=diagnostic) - tests.append(assembly.test) + test: CompiledSqlTest = assembly.test + payload: object = test_input.payload + if isinstance(payload, CompileModelSqlTestInputPayload) and payload.macro_mocks: + report_native_fallback( + site=NativeFallbackSite.SQL_TEST_MACRO_MOCK_QUERIES, + kind=SQL_TEST_EXTENSION_CALLBACK, + ) + test = _with_model_queries(test=test, queries=macro_mock_queries(test_input)) + if assembly.failure is not None and assembly.failure.kind == SQL_TEST_DECIMAL_OVERFLOW: + raise decimal.Overflow([decimal.Overflow]) + tests.append(test) return tuple(tests) + + +def _with_model_queries(*, test: CompiledSqlTest, queries: dict[str, str]) -> CompiledSqlTest: + payload: object = test.payload + if not isinstance(payload, CompiledModelSqlTestPayload): + return test + return replace(test, payload=replace(payload, model_query_overrides=queries)) diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py index 69f6f5f7d3..82822230b0 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py @@ -20,7 +20,6 @@ TABLE_FN_TEST_CTE_PREFIX, ) from sqlbuild.compiler.compile.models import ( - CompileModelInput, CompileModelSqlTestCtes, CompilerDiagnostic, CompileSqlReference, @@ -46,22 +45,6 @@ DBT_REF_TEST_CTE_PREFIX, TABLE_FN_TEST_CTE_PREFIX, ) -_RELATION_REFERENCE_KINDS: frozenset[SqlReferenceKind] = frozenset( - { - SqlReferenceKind.REF, - SqlReferenceKind.SOURCE, - SqlReferenceKind.SEED, - SqlReferenceKind.DBT_REF, - SqlReferenceKind.TABLE_FUNCTION, - } -) -_MOCK_PREFIX_BY_KIND: dict[SqlReferenceKind, str] = { - SqlReferenceKind.REF: REF_TEST_CTE_PREFIX, - SqlReferenceKind.SOURCE: SOURCE_TEST_CTE_PREFIX, - SqlReferenceKind.SEED: SEED_TEST_CTE_PREFIX, - SqlReferenceKind.DBT_REF: DBT_REF_TEST_CTE_PREFIX, - SqlReferenceKind.TABLE_FUNCTION: TABLE_FN_TEST_CTE_PREFIX, -} _IDENTIFIER_PATTERN: re.Pattern[str] = re.compile(r"[A-Za-z_][\w$]*") @@ -323,86 +306,6 @@ def _mock_name_read( return None -def report_mocks_reading_referencing_helpers( - *, - authored_ctes: tuple[CompileSqlTestCte, ...], - reader_ctes: tuple[CompileSqlTestCte, ...], - model_inputs: tuple[CompileModelInput, ...], - target_model_names: tuple[str, ...], - mock_model_names: tuple[str, ...], - test_file: DiscoveredSqlTestFile, - test_block: DiscoveredSqlTestBlock, - syntax: SqlLexicalSyntax, -) -> None: - """Report mocks the test reads that read a helper calling a reference, which cannot resolve.""" - - referencing: dict[str, CompileSqlReference] = {} - for cte in authored_ctes: - if cte.name.startswith(_MOCK_CTE_PREFIXES): - continue - reference: CompileSqlReference | None = next( - ( - reference - for reference in _references(sql=cte.sql_body, syntax=syntax) - if reference.ref_kind in _RELATION_REFERENCE_KINDS - ), - None, - ) - if reference is not None: - referencing[cte.name.casefold()] = reference - if not referencing: - return - graph: SqlTestCteGraph = sql_test_cte_graph( - authored_ctes=authored_ctes, reader_ctes=reader_ctes - ) - called_mocks: list[str] = [] - for sql in ( - *(cte.sql_body for cte in reader_ctes), - *(graph.ctes[key].sql_body for key in _read_helper_keys(graph)), - ): - for reference in _references(sql=sql, syntax=syntax): - called_mocks.extend( - _mock_keys(kind=SqlReferenceKind(reference.ref_kind), reference=reference) - ) - used_mocks: tuple[str, ...] = _used_mock_keys( - graph=graph, - called_mocks=tuple(called_mocks), - model_references={ - model_input.model_file.file_path.stem: model_input.references - for model_input in model_inputs - }, - target_model_names=target_model_names, - mocked=frozenset(mock_model_names), - ) - reporter: _HelperDiagnostics = _HelperDiagnostics(test_file=test_file, test_block=test_block) - for mock_key in used_mocks: - helper_key: str | None = next( - ( - key - for key in reachable_cte_keys(graph=graph, roots=graph.reads.get(mock_key, ())) - if key in referencing - ), - None, - ) - if helper_key is None: - continue - mock_name: str = graph.ctes[mock_key].name - helper_name: str = graph.ctes[helper_key].name - call: str = _reference_call(referencing[helper_key]) - reporter.report( - cte_name=helper_name, - call=call, - message=( - f"SQL test mock '{mock_name}' reads helper CTE '{helper_name}', which calls " - f"{call}; mocks and fixtures are defined before the models the test runs, so " - "the helper cannot be resolved for them" - ), - help=_mock_reference_help( - mock_name=mock_name, call=call, reference=referencing[helper_key] - ), - ) - - def _reader_ctes(payload: CompileModelSqlTestCtes) -> tuple[CompileSqlTestCte, ...]: return (*payload.expected_ctes, *payload.assertion_ctes) @@ -415,63 +318,6 @@ def _read_helper_keys(graph: SqlTestCteGraph) -> tuple[str, ...]: ) -def _used_mock_keys( - *, - graph: SqlTestCteGraph, - called_mocks: tuple[str, ...], - model_references: dict[str, tuple[CompileSqlReference, ...]], - target_model_names: tuple[str, ...], - mocked: frozenset[str], -) -> tuple[str, ...]: - """Return mocks the test's checks and read helpers, or the models it runs, read in order.""" - - used: list[str] = [ - key - for key in reachable_cte_keys(graph=graph, roots=graph.reader_reads) - if graph.ctes[key].name.startswith(_MOCK_CTE_PREFIXES) - ] - used.extend(key for key in dict.fromkeys(called_mocks) if key in graph.ctes and key not in used) - pending: list[str] = [name for name in target_model_names if name not in mocked] - visited: set[str] = set(pending) - while pending: - model_name: str = pending.pop(0) - for reference in model_references.get(model_name, ()): - kind: SqlReferenceKind = SqlReferenceKind(reference.ref_kind) - for mock_key in _mock_keys(kind=kind, reference=reference): - if mock_key in graph.ctes and mock_key not in used: - used.append(mock_key) - if ( - kind is SqlReferenceKind.REF - and reference.ref_name not in mocked - and reference.ref_name not in visited - ): - visited.add(reference.ref_name) - pending.append(reference.ref_name) - return tuple(used) - - -def _mock_keys(*, kind: SqlReferenceKind, reference: CompileSqlReference) -> tuple[str, ...]: - prefix: str | None = _MOCK_PREFIX_BY_KIND.get(kind) - if prefix is None: - return () - names: list[str] = [reference.ref_name] - if reference.ref_package is not None: - names.append(f"{reference.ref_package}__{reference.ref_name}") - return tuple(f"{prefix}{name}".casefold() for name in names) - - -def _mock_reference_help(*, mock_name: str, call: str, reference: CompileSqlReference) -> str: - kind: SqlReferenceKind = SqlReferenceKind(reference.ref_kind) - if kind is not SqlReferenceKind.TABLE_FUNCTION and reference.ref_package is None: - mock_cte: str = f"{kind.fixture_cte_prefix}{reference.ref_name}" - return ( - f"Read a mock by its CTE name instead, for example FROM {mock_cte} rather than " - f"FROM {call}, defining {mock_cte} AS (SELECT ...) if the test does not mock it, " - f"or write the rows of '{mock_name}' directly." - ) - return f"Write the rows of '{mock_name}' directly, for example {mock_name} AS (SELECT ...)." - - def _reference_call(reference: CompileSqlReference) -> str: kind: SqlReferenceKind = SqlReferenceKind(reference.ref_kind) if kind is SqlReferenceKind.DBT_REF and reference.ref_package is not None: diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/identity.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/identity.py deleted file mode 100644 index 74ca533181..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/identity.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Version identity for one expanded SQL test case.""" - -from __future__ import annotations - -import hashlib -import json -from pathlib import Path - -from sqlbuild.compiler.compile.models import CompiledObjectKey, CompiledSqlTestResource -from sqlbuild.compiler.discovery.models import SqlTestParameterDeclaration -from sqlbuild.sql_values.main.identity import sql_value_identity -from sqlbuild.sql_values.models import SqlValue - - -def build_sql_test_case_fingerprint( - *, - source_path: Path, - block_index: int, - case_name: str, - parameter_schema: tuple[SqlTestParameterDeclaration, ...], - parameter_values: tuple[tuple[str, SqlValue], ...], - expanded_sql: str, - scope_deps: tuple[CompiledObjectKey, ...], - tested_resources: tuple[CompiledSqlTestResource, ...], -) -> str: - """Hash all compile-resolved inputs that version a stable test case.""" - - payload: dict[str, object] = { - "source_path": source_path.as_posix(), - "block_index": block_index, - "case_name": case_name, - "parameter_schema": [ - (parameter.name, parameter.value_type.value, parameter.nullable) - for parameter in parameter_schema - ], - "parameter_values": [ - (name, sql_value_identity(value=value)) for name, value in parameter_values - ], - "expanded_sql": expanded_sql, - "scope_deps": sorted((str(dep.resource_type), dep.name) for dep in scope_deps), - "tested_resources": sorted( - (resource.kind.value, resource.name) for resource in tested_resources - ), - } - encoded: str = json.dumps(payload, ensure_ascii=True, separators=(",", ":"), sort_keys=True) - return hashlib.sha256(encoded.encode("utf-8")).hexdigest() diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py deleted file mode 100644 index 38463bfeae..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Scope dependencies of macro, UDF and table-function SQL tests.""" - -from __future__ import annotations - -from sqlbuild.compiler.compile._helpers.render.macros import find_macro_call_names -from sqlbuild.compiler.compile.models import CompiledObjectKey, CompileModelInput -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.references.types import SqlReferenceKind - - -def macro_sql_test_scope_deps( - *, tested_macro_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] -) -> tuple[CompiledObjectKey, ...]: - """Return the models whose macro calls include a tested macro.""" - - tested_names: frozenset[str] = frozenset(tested_macro_names) - scope_deps: list[CompiledObjectKey] = [] - model_input: CompileModelInput - for model_input in model_inputs: - model_macro_deps: frozenset[str] = frozenset( - model_input.macro_deps or find_macro_call_names(model_input.macro_source_sql) - ) - if not tested_names.intersection(model_macro_deps): - continue - scope_deps.append( - CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, - name=model_input.model_file.file_path.stem, - ) - ) - return tuple(scope_deps) - - -def udf_sql_test_scope_deps( - *, tested_udf_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] -) -> tuple[CompiledObjectKey, ...]: - """Return the models that call a tested UDF.""" - - tested_names: frozenset[str] = frozenset(tested_udf_names) - scope_deps: list[CompiledObjectKey] = [] - model_input: CompileModelInput - for model_input in model_inputs: - model_udf_deps: frozenset[str] = frozenset( - reference.ref_name - for reference in model_input.references - if reference.ref_kind == SqlReferenceKind.UDF - ) - if not tested_names.intersection(model_udf_deps): - continue - scope_deps.append( - CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, - name=model_input.model_file.file_path.stem, - ) - ) - return tuple(scope_deps) - - -def function_sql_test_scope_deps( - *, tested_function_names: tuple[str, ...] -) -> tuple[CompiledObjectKey, ...]: - """Return the tested table functions as scope dependencies.""" - - return tuple( - CompiledObjectKey(resource_type=CompiledResourceType.TABLE_FN, name=function_name) - for function_name in tested_function_names - ) diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index a79f8116d7..f680118343 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -30,14 +30,13 @@ class CompilerStage(StrEnum): class NativeFallbackSite(StrEnum): - """A place where a shipped native stage still hands its work to the Python implementation.""" + """A place where a shipped native stage still hands its work to the Python implementation, + or runs a user's Python extension such as a macro.""" SCOPE_REBIND_LOOKUP = "declaration_scopes.rebind_lookup" LINT_EXPANSION = "model_loop.lint_expansion" - MACRO_CALL_SCAN = "macro_calls.scan" - MACRO_CALL_RESOLUTION = "macro_calls.resolution" MACRO_UNBRIDGED_EXPANSION = "macro_calls.unbridged_expansion" MACRO_BRIDGE_UNAVAILABLE = "macro_calls.bridge_unavailable" MACRO_CALL_MOCKED = "macro_calls.mocked_evaluation" PROJECT_ASSEMBLY = "project_assembly.assembly" - SQL_TEST_ASSEMBLY = "sql_test_glue.assembly" + SQL_TEST_MACRO_MOCK_QUERIES = "sql_test_glue.macro_mock_queries" diff --git a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py index 0cf496ca68..7d91e74c2c 100644 --- a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py @@ -14,8 +14,6 @@ LoadedMacro, MacroContext, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge._helpers.store_environment import ( digest_module_files, module_digests_metadata, @@ -30,7 +28,13 @@ macro_store_token, ) from sqlbuild.compiler.macro_bridge.constants import MACRO_CALL_STORE_FILE_NAME -from sqlbuild.compiler.macro_bridge.models import MacroCallClass, MacroCallSite, ModuleSources +from sqlbuild.compiler.macro_bridge.models import ( + MacroCallClass, + MacroCallScan, + MacroCallSite, + MacroScanFailure, + ModuleSources, +) from sqlbuild.compiler.macro_bridge.types import MacroCallEvent, MacroCallRecord from sqlbuild.compiler.scopes.models import DeclarationIdentity from sqlbuild.python_nodes.models import SqlResourceRef @@ -132,24 +136,28 @@ def _observe_modules(self) -> bool: self._module_digests.update(digests) return True - def scan(self, sql: str) -> tuple[MacroCallSite, ...] | None: - """Return the top-level call sites of `sql`, or None when Python must expand it.""" + def scan(self, sql: str) -> MacroCallScan: + """The complete top-level call sites of `sql`, and where its scan raises, if it does.""" - rows: list[tuple[int, int, str, list[str], bool]] | None = _native.scan_macro_call_sites( + rows: list[tuple[int, int, str, list[str] | None, bool]] + failure: tuple[int | None, str] | None + rows, failure = _native.scan_macro_call_sites( sql, self._python_version, self._unicode_version ) - if rows is None: - report_native_fallback(site=NativeFallbackSite.MACRO_CALL_SCAN) - return None - return tuple( - MacroCallSite( - start=start, - end=end, - name=name, - tree_names=tuple(tree_names), - typed_reference_text=typed_reference_text, - ) - for start, end, name, tree_names, typed_reference_text in rows + return MacroCallScan( + sites=tuple( + MacroCallSite( + start=start, + end=end, + name=name, + tree_names=None if tree_names is None else tuple(tree_names), + typed_reference_text=typed_reference_text, + ) + for start, end, name, tree_names, typed_reference_text in rows + ), + failure=None + if failure is None + else MacroScanFailure(call_start=failure[0], message=failure[1]), ) def call_class( @@ -162,6 +170,8 @@ def call_class( ) -> MacroCallClass | None: """Return what a call's result may depend on besides its text, or None if unresolvable.""" + if site.tree_names is None: + return None owner: object = loaded_macros if declarations is None else declarations macros: Mapping[str, LoadedMacro] = ( loaded_macros if declarations is None else declarations.macros diff --git a/src/sqlbuild/compiler/macro_bridge/models.py b/src/sqlbuild/compiler/macro_bridge/models.py index 91ece77cc5..9bafeeb6f5 100644 --- a/src/sqlbuild/compiler/macro_bridge/models.py +++ b/src/sqlbuild/compiler/macro_bridge/models.py @@ -13,10 +13,27 @@ class MacroCallSite: start: int end: int name: str - tree_names: tuple[str, ...] + tree_names: tuple[str, ...] | None + """Every macro name of the call; None where a nested call is not one the scan completes.""" typed_reference_text: bool +@dataclass(frozen=True) +class MacroScanFailure: + """Where Python's scan of a string raises: in the call at `call_start`, or between calls.""" + + call_start: int | None + message: str + + +@dataclass(frozen=True) +class MacroCallScan: + """A string's complete top-level call sites in order, and where its scan raises, if it does.""" + + sites: tuple[MacroCallSite, ...] + failure: MacroScanFailure | None + + @dataclass(frozen=True) class MacroCallClass: """A call's inputs besides its text: by object identity for the memo, by value for the store.""" diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py index cb452078f9..c3b1d80537 100644 --- a/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py +++ b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py @@ -2,6 +2,7 @@ from __future__ import annotations +from dataclasses import replace from pathlib import Path from sqlbuild.compiler.compile.constants import SQL_TEST_HELPER_REFERENCE_CODE @@ -21,8 +22,7 @@ DiagnosticPhase, DiagnosticSeverity, ) -from sqlbuild.compiler.sql_test_glue._helpers.deferrals import record_sql_test_assembly_deferral -from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly +from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly, NativeSqlTestFailure from sqlbuild.compiler.sql_test_glue.types import ( NativeSqlTestAssemblyRow, NativeSqlTestDiagnosticRow, @@ -33,16 +33,23 @@ def native_sql_test_assemblies( *, test_inputs: tuple[CompileSqlTestInput, ...], rows: list[NativeSqlTestAssemblyRow] -) -> tuple[NativeSqlTestAssembly | None, ...]: - """One assembly per test input, or None, recorded, where the native assembly defers.""" +) -> tuple[NativeSqlTestAssembly, ...]: + """One assembly per test input, with the error its assembly raises, if any.""" - assemblies: list[NativeSqlTestAssembly | None] = [] - for test_input, (facts, deferral) in zip(test_inputs, rows, strict=True): + assemblies: list[NativeSqlTestAssembly] = [] + for test_input, (facts, failure_row) in zip(test_inputs, rows, strict=True): + failure: NativeSqlTestFailure | None = ( + None + if failure_row is None + else NativeSqlTestFailure(kind=failure_row[0], message=failure_row[1]) + ) if facts is None: - record_sql_test_assembly_deferral(kind=str(deferral)) - assemblies.append(None) + assemblies.append(NativeSqlTestAssembly(test=None, diagnostics=(), failure=failure)) continue - assemblies.append(native_sql_test_assembly(test_input=test_input, facts=facts)) + assembled: NativeSqlTestAssembly = native_sql_test_assembly( + test_input=test_input, facts=facts + ) + assemblies.append(replace(assembled, failure=failure)) return tuple(assemblies) diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py deleted file mode 100644 index bf25fb8907..0000000000 --- a/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Debug-only record of SQL tests the native assembly hands back to Python.""" - -from __future__ import annotations - -import json -import os -from pathlib import Path - -from sqlbuild.compiler.lineage.constants import ANALYSIS_DEFERRAL_RECORD_PREFIX -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR -from sqlbuild.compiler.sql_test_glue.constants import SQL_TEST_ASSEMBLY_DEFERRAL_SITE - - -def record_sql_test_assembly_deferral(*, kind: str) -> None: - """Append one `{"kind", "site"}` line when the analysis record directory is set.""" - - directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) - if not directory: - return - target: Path = Path(directory) - target.mkdir(parents=True, exist_ok=True) - record: str = json.dumps({"kind": kind, "site": SQL_TEST_ASSEMBLY_DEFERRAL_SITE}) - with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: - _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/sql_test_glue/constants.py b/src/sqlbuild/compiler/sql_test_glue/constants.py index 8c14439660..78d8b5b284 100644 --- a/src/sqlbuild/compiler/sql_test_glue/constants.py +++ b/src/sqlbuild/compiler/sql_test_glue/constants.py @@ -3,4 +3,6 @@ from __future__ import annotations CURSOR_INTRINSIC_NAMES: tuple[str, ...] = ("__cursor_start", "__cursor_end") -SQL_TEST_ASSEMBLY_DEFERRAL_SITE: str = "sql_test_assembly" +SQL_TEST_INPUT_FAILURE: str = "input" +SQL_TEST_DECIMAL_OVERFLOW: str = "decimal_overflow" +SQL_TEST_EXTENSION_CALLBACK: str = "extension_callback" diff --git a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py index 30b1e4dd08..b40a25cde0 100644 --- a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py +++ b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py @@ -12,10 +12,8 @@ from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestAssemblyRow -def assemble_native_sql_tests( - *, inputs: CompileProjectInputs -) -> tuple[NativeSqlTestAssembly | None, ...]: - """Return one assembly per test input, or None for each test Python must assemble.""" +def assemble_native_sql_tests(*, inputs: CompileProjectInputs) -> tuple[NativeSqlTestAssembly, ...]: + """Return one assembly per test input, with the error its assembly raises, if any.""" rows: list[NativeSqlTestAssemblyRow] = native_module.assemble_compiled_sql_tests( NativeSqlTestAssemblyRequest( diff --git a/src/sqlbuild/compiler/sql_test_glue/models.py b/src/sqlbuild/compiler/sql_test_glue/models.py index 69403cb491..5e384c5579 100644 --- a/src/sqlbuild/compiler/sql_test_glue/models.py +++ b/src/sqlbuild/compiler/sql_test_glue/models.py @@ -11,6 +11,7 @@ CompilerDiagnostic, CompileSqlTestInput, ) +from sqlbuild.compiler.sql_test_glue.types import SqlTestAssemblyFailureKind @dataclass(frozen=True) @@ -49,9 +50,19 @@ class NativeSqlTestAssemblyRequest: lexical_syntax: dict[str, object] +@dataclass(frozen=True) +class NativeSqlTestFailure: + """The error one test's assembly raises: its kind and message.""" + + kind: SqlTestAssemblyFailureKind + message: str + + @dataclass(frozen=True) class NativeSqlTestAssembly: - """One natively assembled test and the keyed diagnostics to report before it.""" + """One natively assembled test, the keyed diagnostics to report before it, and the error its + assembly raises: before the test where it has none, else after its macro-mock queries.""" - test: CompiledSqlTest + test: CompiledSqlTest | None diagnostics: tuple[tuple[tuple[str, ...], CompilerDiagnostic], ...] + failure: NativeSqlTestFailure | None = None diff --git a/src/sqlbuild/compiler/sql_test_glue/types.py b/src/sqlbuild/compiler/sql_test_glue/types.py index 3e995450c1..d8bc6a8a6d 100644 --- a/src/sqlbuild/compiler/sql_test_glue/types.py +++ b/src/sqlbuild/compiler/sql_test_glue/types.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Literal + type CtePairRow = tuple[str, str] type NativeChainStepRow = tuple[ str, @@ -31,4 +33,7 @@ str, list[NativeSqlTestDiagnosticRow], ] -type NativeSqlTestAssemblyRow = tuple[NativeSqlTestFactsRow | None, str | None] +type SqlTestAssemblyFailureKind = Literal["input", "internal", "decimal_overflow"] +type NativeSqlTestAssemblyRow = tuple[ + NativeSqlTestFactsRow | None, tuple[SqlTestAssemblyFailureKind, str] | None +] diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 8bf73b344c..90f944d542 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -99,10 +99,10 @@ def _perturbed(self, **changes): _record.write(json.dumps({"kind": "legacy_fallback", "site": "orders.sql"}) + "\\n") """ -MACRO_RESOLUTION_SABOTAGE: str = """ -from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge +MACRO_BRIDGE_SABOTAGE: str = """ +import sqlbuild._native as native -MacroBridge.call_class = lambda self, **_: None +native.native_text_supported = lambda *_args: False """ MODEL_ANALYSIS_SABOTAGE: str = """ diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 10e2ebbe1e..86cc33e235 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -9,7 +9,7 @@ from scripts.compiler_differential.main.differential import run_compiler_differential from tests.e2e.scripts.compiler_differential._test_types import NativeFallbackGateTestCase from tests.e2e.scripts.compiler_differential.helpers import ( - MACRO_RESOLUTION_SABOTAGE, + MACRO_BRIDGE_SABOTAGE, MODEL_ANALYSIS_SABOTAGE, harness_arguments, write_native_perturbation, @@ -43,13 +43,13 @@ ), ), NativeFallbackGateTestCase( - description="shipped_macro_resolution_sabotaged_to_python_fails", - perturbation=MACRO_RESOLUTION_SABOTAGE, + description="shipped_macro_bridge_sabotaged_to_python_fails", + perturbation=MACRO_BRIDGE_SABOTAGE, appended_entries="", expected_exit_code=1, expected_lines=( "Native fallback allow-list: native-preview macro_calls " - "macro_calls.resolution deferred (project): ", + "macro_calls.bridge_unavailable deferred (project): ", "not on the allow-list; port it or list it with a reason", ), ), diff --git a/tests/goldens/python_oracle/sql_test_assembly_a_decimal_case_parameter_the_default_context_overflows.json b/tests/goldens/python_oracle/sql_test_assembly_a_decimal_case_parameter_the_default_context_overflows.json new file mode 100644 index 0000000000..82e0cdc498 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_a_decimal_case_parameter_the_default_context_overflows.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "74234e98afe7498fb5daf1f3", +"raised": "eda90da258aa7debe1d39bd6", +"sql_tests": "74234e98afe7498fb5daf1f3" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json b/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json new file mode 100644 index 0000000000..cebfde29f7 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "97401f24e7899c2504f00597", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "e0d1db85219183b51c102852" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_a_mock_reading_a_deeply_nested_helper_whose_nested_cte_shadows_another.json b/tests/goldens/python_oracle/sql_test_assembly_a_mock_reading_a_deeply_nested_helper_whose_nested_cte_shadows_another.json new file mode 100644 index 0000000000..efb18880b9 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_a_mock_reading_a_deeply_nested_helper_whose_nested_cte_shadows_another.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "134b5a980760bc0b6c888571", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "85aa78137e27cd6c6ebae02b" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_an_unread_helper_with_an_invalid_reference_call.json b/tests/goldens/python_oracle/sql_test_assembly_an_unread_helper_with_an_invalid_reference_call.json new file mode 100644 index 0000000000..4e73795ae1 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_an_unread_helper_with_an_invalid_reference_call.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "d66d63d2d739d2ad7c978860", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "04c58ad3582f31de5ebe5178" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_decimal_case_parameters_the_default_context_rounds_and_flushes.json b/tests/goldens/python_oracle/sql_test_assembly_decimal_case_parameters_the_default_context_rounds_and_flushes.json new file mode 100644 index 0000000000..8918f01755 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_decimal_case_parameters_the_default_context_rounds_and_flushes.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "134b5a980760bc0b6c888571", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "c9fdab56e644c9475570663d" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_model_direct_parameterized_and_mock_reads_helper_tests.json b/tests/goldens/python_oracle/sql_test_assembly_model_direct_parameterized_and_mock_reads_helper_tests.json new file mode 100644 index 0000000000..26f41f75ed --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_model_direct_parameterized_and_mock_reads_helper_tests.json @@ -0,0 +1,34 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "4b16299f17e60e58f2790f5d", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "571161f404b1e68fb08c2be8" +} +], +[ +"sql_test_assembly", +{ +"diagnostics": "2f09ddab1196ed38909b55d2", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "b1fde08fd97d16270d71db00" +} +], +[ +"sql_test_assembly", +{ +"diagnostics": "775db3dd642a88a84b585e70", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "eb8781590978f943d4cda3a2" +} +], +[ +"sql_test_assembly", +{ +"diagnostics": "1a90c1b182791e84c6f3df11", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "9a64536c2908bed9e46d362e" +} +] +] diff --git a/tests/goldens/python_oracle/sql_test_assembly_non_ascii_test_contents_case_folded_reads_and_located_diagnostics.json b/tests/goldens/python_oracle/sql_test_assembly_non_ascii_test_contents_case_folded_reads_and_located_diagnostics.json new file mode 100644 index 0000000000..cf49c48317 --- /dev/null +++ b/tests/goldens/python_oracle/sql_test_assembly_non_ascii_test_contents_case_folded_reads_and_located_diagnostics.json @@ -0,0 +1,10 @@ +[ +[ +"sql_test_assembly", +{ +"diagnostics": "841354a5025b7303e999ad10", +"raised": "74234e98afe7498fb5daf1f3", +"sql_tests": "ba1a28153c7e0394be6b4d5e" +} +] +] diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py index 335268b99e..28207b6a74 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py @@ -7,7 +7,7 @@ @dataclass(frozen=True) class GeneratedSqlTestAssemblyParityTestCase: - """Seeded SQL-test projects native and Python assembly must compile identically.""" + """Seeded SQL-test projects native assembly must compile as Python's recorded assembly did.""" description: str seed: int @@ -15,3 +15,12 @@ class GeneratedSqlTestAssemblyParityTestCase: test_count: int shape: SqlTestCorpusShape expected_native_assemblies: dict[str, int] + + +@dataclass(frozen=True) +class EdgeSqlTestAssemblyTestCase: + """One project whose SQL test Python's assembly once answered alone, and its outcome kind.""" + + description: str + files: dict[str, str] + expected_outcome: str diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index 8bf5f4fd43..d26d004b90 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -17,6 +17,7 @@ from sqlbuild.compiler.compile._helpers.native_stages import sql_tests as sql_test_stage from sqlbuild.compiler.compile.models import ( CompiledProject, + CompiledSqlTest, CompileProjectInputs, ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs @@ -28,6 +29,7 @@ from sqlbuild.compiler.sql_test_glue.types import ( NativeSqlTestAssemblyRow, ) +from tests.integration.src.sqlbuild.compiler.golden_views import GoldenEntry, golden_entry @dataclass(frozen=True) @@ -186,6 +188,24 @@ def generated_sql_test_files( ) -> dict[str, str]: """A project of mocked, expected, asserted, helper, direct and failing SQL tests.""" + files: dict[str, str] = _project_files() + files.update( + (f"tests/unit/test_generated_{index}.sql", _model_test(rng=rng, index=index, shape=shape)) + for index in range(test_count) + ) + files.update( + compress( + ( + (f"tests/unit/test_direct_{index}.sql", sql) + for index, sql in enumerate(_DIRECT_TESTS) + ), + [rng.random() < _DIRECT_TEST_SHARE for _ in _DIRECT_TESTS], + ) + ) + return files + + +def _project_files() -> dict[str, str]: files: dict[str, str] = { "sqlbuild_project.toml": _PROJECT_TOML, "sources/raw.yml": _SOURCES, @@ -200,22 +220,66 @@ def generated_sql_test_files( (f"models/{name}.sql", f'MODEL (description "Model {name}.");\n\n{sql}\n') for name, sql in _BASE_MODELS.items() ) - files.update( - (f"tests/unit/test_generated_{index}.sql", _model_test(rng=rng, index=index, shape=shape)) - for index in range(test_count) - ) - files.update( - compress( - ( - (f"tests/unit/test_direct_{index}.sql", sql) - for index, sql in enumerate(_DIRECT_TESTS) - ), - [rng.random() < _DIRECT_TEST_SHARE for _ in _DIRECT_TESTS], - ) - ) return files +def edge_sql_test_files(*, extra_files: dict[str, str]) -> dict[str, str]: + """The planning project's models, sources and functions plus `extra_files`.""" + + return {**_project_files(), **extra_files} + + +_DEEP_SUBQUERIES: int = 100 +EDGE_UNICODE_CONTENTS: str = ( + 'TEST (name "unicode_contents");\n\nWITH\n' + "-- r\u00e9sum\u00e9: \u212aelvin_rows AS (a comment, not the header)\n" + "kelvin_rows AS (\n" + " SELECT order_id, amount, 'caf\u00e9 \u2603' AS note " + 'FROM __source("raw_orders")\n),\n' + "via_rows AS (SELECT *, '\u017f' AS long_s FROM kelvin_rows),\n" + "__source__raw_orders AS (SELECT * FROM via_rows),\n" + "__expected__stg_orders AS (SELECT order_id FROM KELVIN_ROWS)\nSELECT 1\n" +) +EDGE_DEEP_HELPER: str = ( + 'TEST (name "deep_helper");\n\nWITH\n' + "deep_rows AS (SELECT * FROM " + + "(SELECT * FROM " * _DEEP_SUBQUERIES + + "(WITH shadow AS (SELECT 1 AS order_id) SELECT * FROM shadow) AS s" + + ") AS d" * _DEEP_SUBQUERIES + + "),\n" + 'shadow AS (SELECT order_id, amount FROM __source("raw_orders")),\n' + "__source__raw_orders AS (SELECT * FROM deep_rows),\n" + "__expected__stg_orders AS (SELECT 1 AS order_id)\nSELECT 1\n" +) +EDGE_DECIMAL_CONTEXT: str = ( + 'TEST (name "decimal_context", parameters (p_decimal (type decimal, nullable true)), ' + 'cases (rounded (p_decimal "1.23456789012345678901234567895"), ' + 'carried (p_decimal "9.99999999999999999999999999995E+999998"), ' + 'subnormal (p_decimal "123456E-1000030"), ' + 'flushed (p_decimal "-6E-1000028"), ' + 'half_even (p_decimal "5E-1000027")));\n\n' + 'WITH\n__source__raw_orders AS (SELECT 1 AS order_id, @param("p_decimal") AS p),\n' + '__assert__stg_orders_rows AS (SELECT * FROM __ref("stg_orders") WHERE 1 = 0)\nSELECT 1\n' +) +EDGE_DECIMAL_OVERFLOW: str = EDGE_DECIMAL_CONTEXT.replace( + '"5E-1000027"', '"9.99999999999999999999999999995E+999999"' +) +EDGE_UNICODE_MACRO_SCOPE: dict[str, str] = { + "models/weird_amounts.sql": ( + 'MODEL (description "Model weird_amounts.");\n\n' + 'WITH o AS (SELECT amount FROM __source("raw_orders"))\nSELECT amount@\u00f6re FROM o\n' + ), + "tests/unit/test_direct_macro.sql": _DIRECT_TESTS[0], +} +EDGE_INVALID_UNREAD_HELPER: str = ( + 'TEST (name "invalid_unread_helper");\n\nWITH\n' + "bad_rows AS (SELECT * FROM __ref(1)),\n" + 'source_rows AS (SELECT order_id FROM __source("raw_orders")),\n' + "__source__raw_orders AS (SELECT * FROM source_rows),\n" + "__expected__stg_orders AS (SELECT 1 AS order_id)\nSELECT 1\n" +) + + def _model_test(*, rng: random.Random, index: int, shape: SqlTestCorpusShape) -> str: """One model test: the first target's first mock, then random mocks, helper and checks. @@ -393,21 +457,6 @@ def assembly_outcome(*, project_dir: Path) -> PlanningCallOutcome: return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) -def python_assembly_outcome( - *, project_dir: Path, monkeypatch: pytest.MonkeyPatch -) -> PlanningCallOutcome: - """The compiled SQL tests and diagnostics with every test deferred to Python's assembly, or - what it raises.""" - - with monkeypatch.context() as patch: - patch.setattr( - sql_test_stage, - "assemble_native_sql_tests", - lambda *, inputs: (None,) * len(inputs.test_inputs), - ) - return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) - - def _compiled_tests_and_diagnostics(*, project_dir: Path) -> tuple[object, object]: project: CompiledProject = build_project_graph( discovered_inputs=discover_project_inputs(project_dir=project_dir), @@ -417,43 +466,52 @@ def _compiled_tests_and_diagnostics(*, project_dir: Path) -> tuple[object, objec def record_native_assemblies(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count tests compile takes from the native assembly, with diagnostics, cases, or defers. + """Count tests compile takes from the native assembly, with diagnostics, cases or failures. - `native_assembled` counts only assemblies the stage seam hands compile; a deferred test - counts under `deferred_` and is assembled by Python, so it never counts as native work. + A test whose assembly raises counts under `failed_`. """ answers: Counter[str] = Counter() assemble: Callable[[NativeSqlTestAssemblyRequest], list[NativeSqlTestAssemblyRow]] = ( native_module.assemble_compiled_sql_tests ) - seam: Callable[..., tuple[NativeSqlTestAssembly | None, ...]] = ( + seam: Callable[..., tuple[NativeSqlTestAssembly, ...]] = ( sql_test_stage.assemble_native_sql_tests ) - def counted_deferrals(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: + def counted_failures(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: rows: list[NativeSqlTestAssemblyRow] = assemble(request) - deferrals: list[str] = list(filter(None, (row[1] for row in rows))) - answers.update(f"deferred_{kind}" for kind in deferrals) + answers.update(f"failed_{failure[0]}" for _, failure in rows if failure is not None) return rows - def counted_assemblies( - *, inputs: CompileProjectInputs - ) -> tuple[NativeSqlTestAssembly | None, ...]: - assemblies: tuple[NativeSqlTestAssembly | None, ...] = seam(inputs=inputs) - native: list[NativeSqlTestAssembly] = list(filter(None, assemblies)) + def counted_assemblies(*, inputs: CompileProjectInputs) -> tuple[NativeSqlTestAssembly, ...]: + assemblies: tuple[NativeSqlTestAssembly, ...] = seam(inputs=inputs) + native: list[CompiledSqlTest] = [item.test for item in assemblies if item.test is not None] answers["native_assembled"] += len(native) - answers["native_with_diagnostics"] += sum(bool(item.diagnostics) for item in native) + answers["native_with_diagnostics"] += sum(bool(item.diagnostics) for item in assemblies) answers["native_case_fingerprints"] += sum( - item.test.case_fingerprint is not None for item in native + test.case_fingerprint is not None for test in native ) return assemblies - monkeypatch.setattr(native_module, "assemble_compiled_sql_tests", counted_deferrals) + monkeypatch.setattr(native_module, "assemble_compiled_sql_tests", counted_failures) monkeypatch.setattr(sql_test_stage, "assemble_native_sql_tests", counted_assemblies) return answers +def assembly_golden_entry(*, outcome: PlanningCallOutcome, project_dir: Path) -> GoldenEntry: + """One project's compiled SQL tests, diagnostics and raised error as a golden entry.""" + + tests, diagnostics = cast( + tuple[object, object], outcome.value if outcome.raised is None else (None, None) + ) + return golden_entry( + "sql_test_assembly", + {"sql_tests": tests, "diagnostics": diagnostics, "raised": outcome.raised}, + masked=(str(project_dir),), + ) + + def outcome_kind(outcome: object) -> str: """`raised` when the call raised, else `answered`; only native rows count as native work.""" diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py index 9e6d75b975..444129c43b 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py @@ -1,4 +1,9 @@ -"""SQL tests assembled natively equal Python's assembly, which answers native deferrals.""" +"""SQL tests assembled natively match the tests, diagnostics and errors recorded from Python's +assembly, which the native assembly replaces. + +Expected outputs were recorded once from the pre-port Python assembly (`_assemble_compiled_sql_test`) +into `tests/goldens/python_oracle/sql_test_assembly_*.json`. +""" from __future__ import annotations @@ -8,20 +13,33 @@ import pytest -from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.sql_test_glue._test_types import ( + EdgeSqlTestAssemblyTestCase, GeneratedSqlTestAssemblyParityTestCase, ) from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import ( + EDGE_DECIMAL_CONTEXT, + EDGE_DECIMAL_OVERFLOW, + EDGE_DEEP_HELPER, + EDGE_INVALID_UNREAD_HELPER, + EDGE_UNICODE_CONTENTS, + EDGE_UNICODE_MACRO_SCOPE, INVERTED_WINDOW, NO_WINDOW, VALID_WINDOW, PlanningCallOutcome, SqlTestCorpusShape, + assembly_golden_entry, assembly_outcome, + edge_sql_test_files, generated_sql_test_assembly_files, outcome_kind, - python_assembly_outcome, record_native_assemblies, write_project, ) @@ -43,12 +61,9 @@ unflattenable_assertion_share=0.1, ), expected_native_assemblies={ - "native_assembled": 181, - "native_with_diagnostics": 17, - "native_case_fingerprints": 96, - "deferred_macro_mocks": 4, - "deferred_non_ascii_text": 11, - "deferred_decimal_context": 5, + "native_assembled": 201, + "native_with_diagnostics": 26, + "native_case_fingerprints": 101, }, ) ], @@ -61,7 +76,7 @@ def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_ ) -> None: rng: random.Random = random.Random(test_case.seed) native_assemblies: Counter[str] = record_native_assemblies(monkeypatch=monkeypatch) - differences: list[tuple[object, object, object]] = [] + entries: list[GoldenEntry] = [] outcomes: Counter[str] = Counter() for index in range(test_case.count): project_dir: Path = tmp_path / f"project_{index}" @@ -71,14 +86,70 @@ def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_ rng=rng, test_count=test_case.test_count, shape=test_case.shape ), ) - expected: PlanningCallOutcome = python_assembly_outcome( - project_dir=project_dir, monkeypatch=monkeypatch - ) actual: PlanningCallOutcome = assembly_outcome(project_dir=project_dir) - differences.extend(mismatches(inputs=[index], expected=[expected], actual=[actual])) + entries.append(assembly_golden_entry(outcome=actual, project_dir=project_dir)) outcomes[outcome_kind(actual)] += 1 - assert (differences, dict(native_assemblies)) == ( - [], - test_case.expected_native_assemblies, - ), outcomes + assert ( + golden_differences( + read_golden(golden_name("sql_test_assembly", test_case.description)), entries + ), + dict(native_assemblies), + ) == ([], test_case.expected_native_assemblies), outcomes + + +@pytest.mark.parametrize( + "test_case", + [ + EdgeSqlTestAssemblyTestCase( + description="non-ASCII test contents, case-folded reads and located diagnostics", + files={"tests/unit/test_unicode.sql": EDGE_UNICODE_CONTENTS}, + expected_outcome="answered", + ), + EdgeSqlTestAssemblyTestCase( + description="a mock reading a deeply nested helper whose nested CTE shadows another", + files={"tests/unit/test_deep.sql": EDGE_DEEP_HELPER}, + expected_outcome="answered", + ), + EdgeSqlTestAssemblyTestCase( + description="decimal case parameters the default context rounds and flushes", + files={"tests/unit/test_decimal.sql": EDGE_DECIMAL_CONTEXT}, + expected_outcome="answered", + ), + EdgeSqlTestAssemblyTestCase( + description="a decimal case parameter the default context overflows", + files={"tests/unit/test_decimal.sql": EDGE_DECIMAL_OVERFLOW}, + expected_outcome="raised", + ), + EdgeSqlTestAssemblyTestCase( + description="a macro test over a model with a non-ASCII name after @", + files=EDGE_UNICODE_MACRO_SCOPE, + expected_outcome="answered", + ), + EdgeSqlTestAssemblyTestCase( + description="an unread helper with an invalid reference call", + files={"tests/unit/test_invalid.sql": EDGE_INVALID_UNREAD_HELPER}, + expected_outcome="answered", + ), + ], + ids=lambda case: case.description, +) +def test_given_edge_sql_test_when_assembling_natively_then_matches_recorded_python( + test_case: EdgeSqlTestAssemblyTestCase, tmp_path: Path +) -> None: + project_dir: Path = tmp_path / golden_name("edge", test_case.description) + write_project(project_dir=project_dir, files=edge_sql_test_files(extra_files=test_case.files)) + + actual: PlanningCallOutcome = assembly_outcome(project_dir=project_dir) + + assert ( + golden_differences( + read_golden(golden_name("sql_test_assembly", test_case.description)), + [assembly_golden_entry(outcome=actual, project_dir=project_dir)], + ), + outcome_kind(actual), + ) == ([], test_case.expected_outcome) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py index 9ec4a839cb..5247a14641 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py @@ -21,13 +21,13 @@ description="counts_per_site_and_kind_sorted", recorded=( (NativeFallbackSite.PROJECT_ASSEMBLY, "deferred"), - (NativeFallbackSite.MACRO_CALL_RESOLUTION, "deferred"), + (NativeFallbackSite.MACRO_CALL_MOCKED, "deferred"), (NativeFallbackSite.PROJECT_ASSEMBLY, "deferred"), (NativeFallbackSite.PROJECT_ASSEMBLY, "error"), ), expected_files=[ [ - ["macro_calls.resolution", "deferred", 1], + ["macro_calls.mocked_evaluation", "deferred", 1], ["project_assembly.assembly", "deferred", 2], ["project_assembly.assembly", "error", 1], ] diff --git a/tests/unit/src/sqlbuild/compiler/macro_bridge/classes/test_macro_bridge.py b/tests/unit/src/sqlbuild/compiler/macro_bridge/classes/test_macro_bridge.py index 758149c91a..bcaccfe10c 100644 --- a/tests/unit/src/sqlbuild/compiler/macro_bridge/classes/test_macro_bridge.py +++ b/tests/unit/src/sqlbuild/compiler/macro_bridge/classes/test_macro_bridge.py @@ -7,7 +7,7 @@ import pytest from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge -from sqlbuild.compiler.macro_bridge.models import MacroCallSite +from sqlbuild.compiler.macro_bridge.models import MacroCallScan from tests.unit.src.sqlbuild.compiler.macro_bridge.classes._test_types import ( DeepNestingScanTestCase, ) @@ -47,7 +47,10 @@ def test_given_deeply_nested_calls_when_scanning_then_the_bridge_returns_promptl sql: str = "@m(" * test_case.depth + "x" + ")" * test_case.depth started: float = time.perf_counter() - sites: tuple[MacroCallSite, ...] | None = bridge.scan(sql) + scan: MacroCallScan = bridge.scan(sql) assert time.perf_counter() - started < test_case.expected_max_seconds - assert tuple(site.tree_names for site in sites or ()) == test_case.expected_tree_names + assert (tuple(site.tree_names for site in scan.sites), scan.failure) == ( + test_case.expected_tree_names, + None, + ) From dbe70e962d015ee5c0b415682f1c03647038d1b0 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:44:43 +0100 Subject: [PATCH 17/36] feat(native): carry project graph facts in the native assembly request Models send name, directory and tags, seeds their tags, and SQL functions their kind, name and tags, so the assembly that computes dependencies can build the project graph. --- .../main/assemble_project_resources.rs | 2 +- .../src/assembly/project/models.rs | 24 ++++++- .../src/assembly/project/tests/helpers.rs | 2 + .../_helpers/project_assembly/resources.rs | 42 ++++++++---- src/sqlbuild/_native.pyi | 14 +++- .../project_assembly/_helpers/request.py | 25 ++++++- .../compiler/project_assembly/types.py | 7 +- .../compiler/project_assembly/_test_types.py | 11 ++++ .../compiler/project_assembly/helpers.py | 16 +++++ .../test_native_project_assembly.py | 66 +++++++++++++++++++ 10 files changed, 186 insertions(+), 23 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs index ce6c488027..bce5c8f38f 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs @@ -58,7 +58,7 @@ fn resources(request: &ProjectRequest) -> Fact { function_deps: request .functions .iter() - .map(|references| reference_deps(references)) + .map(|function| reference_deps(&function.references)) .collect(), audit_deps: request.audits.iter().map(audit_deps).collect::>()?, reads: inputs.reads.into_inner(), diff --git a/crates/sqlbuild-analysis/src/assembly/project/models.rs b/crates/sqlbuild-analysis/src/assembly/project/models.rs index d253255695..4f26e9abc9 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/models.rs @@ -70,9 +70,15 @@ pub enum SyntaxFailure { UnknownDialect(String), } -/// One model's references and the SQL strings its assembly validates. +/// One model's identity, graph facts, references and the SQL strings its assembly validates. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ModelFacts { + /// The model's name: its file stem, the name of its `("model", name)` key. + pub name: String, + /// `str(model_file.relative_path.parent)`, the folder project selectors match. + pub directory: String, + /// `config.values["tags"]` items as `str()` where a list or tuple, else empty. + pub tags: Vec, pub references: Vec, pub syntax_checks: Vec, } @@ -86,14 +92,26 @@ pub struct SourceFacts { pub schema: Option, } -/// One seed's name and authored namespace. +/// One seed's name, tags and authored namespace. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SeedFacts { pub name: String, + /// `schema_entry.tags`. + pub tags: Vec, pub database: Option, pub schema: Option, } +/// One SQL function's key, tags and references. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FunctionFacts { + /// `function_node_type(return_columns)`: `udf` or `table_fn`, its key's resource type. + pub kind: String, + pub name: String, + pub tags: Vec, + pub references: Vec, +} + /// One audit's references and the resource it is attached to. #[derive(Debug, Clone, PartialEq, Eq)] pub struct AuditFacts { @@ -115,7 +133,7 @@ pub struct ProjectRequest { pub models: Vec, pub sources: Vec, pub seeds: Vec, - pub functions: Vec>, + pub functions: Vec, pub audits: Vec, } diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs index e983316a09..10e8587212 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs @@ -42,6 +42,7 @@ pub(crate) fn seed( seed_target( &SeedFacts { name: name.to_owned(), + tags: Vec::new(), database: database.map(str::to_owned), schema: schema.map(str::to_owned), }, @@ -72,6 +73,7 @@ pub(crate) fn seed_reads( let resolved = seed_target( &SeedFacts { name: name.to_owned(), + tags: Vec::new(), database: database.map(str::to_owned), schema: schema.map(str::to_owned), }, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index f82adb197d..f15fd42160 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -10,9 +10,9 @@ use sqlbuild_analysis::assembly::project::main::assemble_project_resources::asse use sqlbuild_analysis::assembly::project::main::check_sql_syntax::check_sql_syntax; use sqlbuild_analysis::assembly::project::main::sql_syntax_error::sql_syntax_error as check_syntax_error; use sqlbuild_analysis::assembly::project::models::{ - AuditFacts, InputRead, ModelFacts, Namespace, ProjectRequest, ProjectResources, Reference, - SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, SyntaxFailure, SyntaxMode, TargetNamespace, - Variable, + AuditFacts, FunctionFacts, InputRead, ModelFacts, Namespace, ProjectRequest, ProjectResources, + Reference, SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, SyntaxFailure, SyntaxMode, + TargetNamespace, Variable, }; use sqlbuild_analysis::assembly::project::types::ObjectKey; use sqlbuild_model_config::templates::models::Scalar; @@ -40,12 +40,20 @@ type DefaultsRow = ( ); /// `(name, kind, text)`; kind `text` (also exact ints), `bool`, `none` or anything else. type VariableRow = (String, String, String); -/// `(references, [(sql, placeholders)])`. -type ModelRow = (Vec, Vec<(String, Pairs)>); +/// `(name, directory, tags, references, [(sql, placeholders)])`. +type ModelRow = ( + String, + String, + Vec, + Vec, + Vec<(String, Pairs)>, +); /// `(name, managed, database, schema)`. type SourceRow = (String, bool, Option, Option); -/// `(name, database, schema)`. -type SeedRow = (String, Option, Option); +/// `(name, tags, database, schema)`. +type SeedRow = (String, Vec, Option, Option); +/// `(kind, name, tags, references)`. +type FunctionRow = (String, String, Vec, Vec); /// `(references, attached (kind, name))`. type AuditRow = (Vec, Option<(String, String)>); /// The dialect, target, defaults, variables, environment and resources one project assembles. @@ -58,7 +66,7 @@ type RequestRow = ( Vec, Vec, Vec, - Vec>, + Vec, Vec, ); /// `(database, schema, logical database, logical schema)`. @@ -183,13 +191,22 @@ fn project_request(request: RequestRow) -> ProjectRequest { .collect(), seeds: seeds .into_iter() - .map(|(name, database, schema)| SeedFacts { + .map(|(name, tags, database, schema)| SeedFacts { name, + tags, database, schema, }) .collect(), - functions: functions.into_iter().map(references).collect(), + functions: functions + .into_iter() + .map(|(kind, name, tags, rows)| FunctionFacts { + kind, + name, + tags, + references: references(rows), + }) + .collect(), audits: audits .into_iter() .map(|(rows, attached)| AuditFacts { @@ -212,8 +229,11 @@ fn variable(row: VariableRow) -> (String, Variable) { } fn model(row: ModelRow) -> ModelFacts { - let (rows, checks) = row; + let (name, directory, tags, rows, checks) = row; ModelFacts { + name, + directory, + tags, references: references(rows), syntax_checks: checks .into_iter() diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index c78108c03a..f7b36e11b1 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -657,10 +657,18 @@ def assemble_project_resource_facts( tuple[str | None, str | None, str | None, str | None], list[tuple[str, str, str]], list[tuple[str, str | None]], - list[tuple[list[tuple[str, str, str | None]], list[tuple[str, list[tuple[str, str]]]]]], + list[ + tuple[ + str, + str, + list[str], + list[tuple[str, str, str | None]], + list[tuple[str, list[tuple[str, str]]]], + ] + ], list[tuple[str, bool, str | None, str | None]], - list[tuple[str, str | None, str | None]], - list[list[tuple[str, str, str | None]]], + list[tuple[str, list[str], str | None, str | None]], + list[tuple[str, str, list[str], list[tuple[str, str, str | None]]]], list[tuple[list[tuple[str, str, str | None]], tuple[str, str] | None]], ], /, diff --git a/src/sqlbuild/compiler/project_assembly/_helpers/request.py b/src/sqlbuild/compiler/project_assembly/_helpers/request.py index 145c5a8964..9ad72253ca 100644 --- a/src/sqlbuild/compiler/project_assembly/_helpers/request.py +++ b/src/sqlbuild/compiler/project_assembly/_helpers/request.py @@ -25,6 +25,7 @@ SyntaxCheckRow, VariableRow, ) +from sqlbuild.compiler.resource_names.main.function_node_type import function_node_type from sqlbuild.spec.contracts.models import DefaultsConfig, TargetConfig @@ -57,7 +58,13 @@ def project_request( [_variable_row(name=name, value=value) for name, value in inputs.effective_vars.items()], _environment_rows((*target_values, *default_values, *seed_values)), [ - (_reference_rows(model_input.references), _syntax_check_rows(checks)) + ( + model_input.model_file.file_path.stem, + str(model_input.model_file.relative_path.parent), + _tag_rows(model_input.config.values.get("tags")), + _reference_rows(model_input.references), + _syntax_check_rows(checks), + ) for model_input, checks in zip(inputs.model_inputs, syntax_checks, strict=True) ], [ @@ -72,13 +79,19 @@ def project_request( [ ( seed_input.schema_entry.name, + list(seed_input.schema_entry.tags), seed_input.schema_entry.database, seed_input.schema_entry.schema, ) for seed_input in inputs.seed_inputs ], [ - _reference_rows(function_input.references) + ( + function_node_type(return_columns=function_input.return_columns), + function_input.name, + list(function_input.tags), + _reference_rows(function_input.references), + ) for function_input in inputs.sql_function_inputs ], [ @@ -92,6 +105,14 @@ def project_request( ) +def _tag_rows(value: object) -> list[str]: + """Model config tags as the project graph indexes them.""" + + if isinstance(value, list | tuple): + return [str(item) for item in value] + return [] + + def _environment_rows(values: Iterable[str | None]) -> list[tuple[str, str | None]]: """Python's own `os.environ` lookup of every name an `ENV:` template may read.""" diff --git a/src/sqlbuild/compiler/project_assembly/types.py b/src/sqlbuild/compiler/project_assembly/types.py index 4583e8bae2..057110b741 100644 --- a/src/sqlbuild/compiler/project_assembly/types.py +++ b/src/sqlbuild/compiler/project_assembly/types.py @@ -5,9 +5,10 @@ type ReferenceRow = tuple[str, str, str | None] type SyntaxCheckRow = tuple[str, list[tuple[str, str]]] type VariableRow = tuple[str, str, str] -type ModelRow = tuple[list[ReferenceRow], list[SyntaxCheckRow]] +type ModelRow = tuple[str, str, list[str], list[ReferenceRow], list[SyntaxCheckRow]] type SourceRow = tuple[str, bool, str | None, str | None] -type SeedRow = tuple[str, str | None, str | None] +type SeedRow = tuple[str, list[str], str | None, str | None] +type FunctionRow = tuple[str, str, list[str], list[ReferenceRow]] type AuditRow = tuple[list[ReferenceRow], tuple[str, str] | None] type TargetRow = tuple[str | None, str | None, str | None] type DefaultsRow = tuple[str | None, str | None, str | None, str | None] @@ -20,7 +21,7 @@ list[ModelRow], list[SourceRow], list[SeedRow], - list[list[ReferenceRow]], + list[FunctionRow], list[AuditRow], ] type ObjectKeyRow = tuple[str, str] diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py index 09878fbcea..1c63ba08d6 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py @@ -55,3 +55,14 @@ class OptOutTestCase: models: dict[str, str] expected_rejected: dict[str, bool] expected_python_validations: int + + +@dataclass(frozen=True) +class GraphFactsTestCase: + """A project whose assembly request carries each resource's project-graph facts.""" + + description: str + files: dict[str, str] + expected_model_tags: tuple[list[str], ...] + expected_seeds: list[tuple[str, list[str]]] + expected_function_kinds: tuple[str, ...] diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py index 3f865b95da..3a498c9d7a 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py @@ -13,6 +13,7 @@ import pytest +import sqlbuild._native as _native from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile._helpers.assembly import project as project_module from sqlbuild.compiler.compile._helpers.assembly.project import assemble_compiled_project @@ -27,6 +28,7 @@ from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine +from sqlbuild.compiler.project_assembly.types import ProjectRequestRow from sqlbuild.sql_values.types import CollectionRendering from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import deferral_kinds @@ -277,6 +279,20 @@ def python_resource_calls(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: return calls +def record_assembly_requests(*, monkeypatch: pytest.MonkeyPatch) -> list[ProjectRequestRow]: + """Record every request the native project assembly receives.""" + + requests: list[ProjectRequestRow] = [] + original: Callable[..., Any] = _native.assemble_project_resource_facts + + def recording(request: ProjectRequestRow) -> Any: + requests.append(request) + return original(request) + + monkeypatch.setattr(_native, "assemble_project_resource_facts", recording) + return requests + + def _write(*, path: Path, contents: str) -> int: path.parent.mkdir(parents=True, exist_ok=True) return path.write_text(contents, encoding="utf-8") diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index 91357b48e5..7db41eecf4 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -13,12 +13,14 @@ from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import CompiledProject, CompileProjectInputs from sqlbuild.compiler.frontier.types import CompilerEngine +from sqlbuild.compiler.project_assembly.types import ProjectRequestRow from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR from tests.integration.src.sqlbuild.compiler.helpers import mismatches from tests.integration.src.sqlbuild.compiler.project_assembly._test_types import ( AssemblyDeferralTestCase, DeferredAssemblyTestCase, GeneratedAssemblyParityTestCase, + GraphFactsTestCase, OptOutTestCase, WindowsEnvironmentTestCase, ) @@ -31,6 +33,7 @@ generated_assembly_files, project_inputs, python_resource_calls, + record_assembly_requests, recorded_assembly, rejected_opt_outs, seed_yml, @@ -335,3 +338,66 @@ def test_given_sql_analysis_opt_outs_when_attaching_natively_then_python_rejecti assert python == (test_case.expected_rejected, test_case.expected_python_validations) assert native == (test_case.expected_rejected, 0) assert assembly_deferrals(record_dir) == Counter() + + +@pytest.mark.parametrize( + "test_case", + [ + GraphFactsTestCase( + description="tagged models in folders, tagged seeds and both function kinds", + files={ + "sqlbuild_project.toml": 'name = "orders"\nadapter = "duckdb"\n', + "models/staging/orders.sql": ( + "MODEL (description 'Orders.', tags [daily, finance]);\n\nSELECT 1 AS id\n" + ), + "models/customers.sql": ( + "MODEL (description 'Customers.');\n\nSELECT id FROM __ref(\"orders\")\n" + ), + "seeds/regions.yml": ( + "seeds:\n - name: regions\n description: Regions.\n" + " tags: [lookup]\n columns:\n - name: n\n type: INTEGER\n" + ), + "seeds/regions.csv": "n\n1\n", + "functions/sql/is_big.sql": ( + "FUNCTION (description 'Big.', arguments (x INTEGER), returns BOOLEAN," + " tags [math]);\n\nx > 100\n" + ), + "functions/sql/order_ids.sql": ( + "FUNCTION (description 'Ids.', arguments (x INTEGER)," + ' returns table (id INTEGER));\n\nSELECT id FROM __ref("orders")\n' + ), + }, + expected_model_tags=([], ["daily", "finance"]), + expected_seeds=[("regions", ["lookup"])], + expected_function_kinds=("udf", "table_fn"), + ) + ], + ids=lambda case: case.description, +) +def test_given_project_when_assembling_natively_then_request_carries_graph_facts( + test_case: GraphFactsTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + requests: list[ProjectRequestRow] = record_assembly_requests(monkeypatch=monkeypatch) + project: CompiledProject = assemble_with( + inputs=project_inputs(project_dir=tmp_path / "project", files=test_case.files), + engine=CompilerEngine.NATIVE, + monkeypatch=monkeypatch, + ) + request: ProjectRequestRow = requests[0] + + assert ( + [(name, directory, tags) for name, directory, tags, _, _ in request[5]], + [(name, tags) for name, tags, _, _ in request[7]], + [(kind, name, tags) for kind, name, tags, _ in request[8]], + ) == ( + [ + (model.key.name, str(model.relative_path.parent)) + (tags,) + for model, tags in zip(project.models, test_case.expected_model_tags, strict=True) + ], + test_case.expected_seeds, + [ + (str(function.key.resource_type), function.key.name, list(function.tags)) + for function in project.functions + ], + ) + assert [kind for kind, _, _, _ in request[8]] == list(test_case.expected_function_kinds) From e54c565cacf06c7271bb68b4e136e4008ee15035 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:56:06 +0100 Subject: [PATCH 18/36] feat(native)!: scan macro call names natively and expand unbridged strings through native call sites find_macro_call_names, find_nested_macro_call_names and the final macro output check use the native scanner. Strings expanded outside compile inputs evaluate their native call sites in order without the memo, as bridged strings with an unresolvable tree do, so Python's top-level call scanner and its macro_calls.unbridged_expansion fallback are deleted. The bridge requires a supported Python, as discovery already does, instead of falling back to the Python expansion. --- Cargo.lock | 1 + crates/sqlbuild-analysis/Cargo.toml | 1 + .../_helpers/sql_tests/macro_call_names.rs | 98 +- .../bindings/_helpers/render/macro_calls.rs | 21 + .../src/macro_calls/_helpers/scanner.rs | 17 + .../src/macro_calls/main/macro_call_names.rs | 35 + .../src/macro_calls/main/mod.rs | 1 + .../sqlbuild-render/src/macro_calls/tests.rs | 2 + .../tests/test_macro_call_names.rs | 49 + src/sqlbuild/_native.pyi | 1 + .../_helpers/macro_bridge/expansion.py | 70 +- .../compile/_helpers/render/macros.py | 165 +- src/sqlbuild/compiler/frontier/types.py | 2 - .../macro_bridge/_helpers/call_sites.py | 63 + .../macro_bridge/classes/macro_bridge.py | 37 +- .../main/run_with_macro_bridge.py | 8 +- .../scripts/compiler_differential/helpers.py | 4 +- .../test_native_fallback_gate.py | 10 +- .../macro_expansion_seeded_fragments.json | 12002 ++++++++++++++++ .../sqlbuild/compiler/compile/_test_types.py | 3 +- .../compile/test_native_macro_bridge.py | 32 +- 21 files changed, 12309 insertions(+), 313 deletions(-) create mode 100644 crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs create mode 100644 crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs create mode 100644 src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py create mode 100644 tests/goldens/python_oracle/macro_expansion_seeded_fragments.json diff --git a/Cargo.lock b/Cargo.lock index 0d533db2ee..b21ba42d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,6 +705,7 @@ dependencies = [ "sqlbuild-cache", "sqlbuild-core", "sqlbuild-model-config", + "sqlbuild-render", "sqlbuild-scopes", "sqlbuild-sqltext", ] diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 4a1d764352..ff48a44396 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -23,6 +23,7 @@ sha2.workspace = true sqlbuild-cache.workspace = true sqlbuild-core.workspace = true sqlbuild-model-config.workspace = true +sqlbuild-render.workspace = true sqlbuild-scopes.workspace = true sqlbuild-sqltext.workspace = true diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs index 3c780ca3ce..e0c42597d8 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs @@ -1,103 +1,15 @@ -//! Python's `find_macro_call_names`: unique top-level macro call names in encounter order. +//! Python's `find_macro_call_names` over a model's pre-macro SQL. use sqlbuild_core::text::main::active_python_text::active_python_text; -use sqlbuild_core::text::main::is_python_alnum::is_python_alnum; -use sqlbuild_core::text::main::is_python_alpha::is_python_alpha; -use sqlbuild_core::text::main::is_python_space::is_python_space; -use sqlbuild_core::text::models::PythonText; -use sqlbuild_sqltext::sql_scan::main::matching_paren::matching_paren; -use sqlbuild_sqltext::sql_scan::main::non_code_end::non_code_end; -use sqlbuild_sqltext::sql_scan::models::{QuotePolicy, Unclosed}; +use sqlbuild_render::macro_calls::main::macro_call_names::macro_call_names as scanned_names; -/// Declaration calls that expansion substitutes rather than running as macros. +/// Declaration calls expansion substitutes rather than running as macros. const DECLARATION_CALLS: [&str; 2] = ["enum", "const"]; -const MISSING_PARENTHESIS: &str = "expected opening parenthesis"; /// The names Python's scan returns, or the message of the `CompileInputError` it raises. pub(crate) fn macro_call_names(sql: &str) -> Result, String> { - let python: PythonText = active_python_text(); - let bytes = sql.as_bytes(); - let mut names: Vec = Vec::new(); - let mut index = 0; - while index < bytes.len() { - if let Some(end) = non_code_end(bytes, index, QuotePolicy::COMPILER).map_err(unclosed)? { - index = end; - continue; - } - if bytes[index] != b'@' || !macro_call_at(python, sql, index) { - index += 1; - continue; - } - let name_end = identifier_end(python, sql, index + 1); - let name = &sql[index + 1..name_end]; - if !names.iter().any(|known| known == name) { - names.push(name.to_owned()); - } - let open = space_end(sql, name_end); - if bytes.get(open) != Some(&b'(') { - return Err(MISSING_PARENTHESIS.to_owned()); - } - index = matching_paren(bytes, open, QuotePolicy::COMPILER).map_err(unclosed)? + 1; - } + let mut names: Vec = scanned_names(active_python_text(), sql, false) + .map_err(|error| error.message().to_owned())?; names.retain(|name| !DECLARATION_CALLS.contains(&name.as_str())); Ok(names) } - -/// The message of the `CompileInputError` Python's macro scan raises for unclosed text. -fn unclosed(error: Unclosed) -> String { - let what = match error { - Unclosed::BlockComment => "block comment", - Unclosed::Quote => "quoted string", - Unclosed::Parenthesis => "parenthesis", - }; - format!("Macro expansion contains an unclosed {what}") -} - -/// Python's `_is_macro_call_start`, which skips whitespace after each name character. -fn macro_call_at(python: PythonText, sql: &str, at: usize) -> bool { - let Some(first) = char_at(sql, at + 1) else { - return false; - }; - if !(first == '_' || is_python_alpha(python, first)) { - return false; - } - let mut cursor = at + 1 + first.len_utf8(); - while let Some(character) = char_at(sql, cursor) { - if !is_identifier_continue(python, character) { - break; - } - cursor = space_end(sql, cursor + character.len_utf8()); - } - sql.as_bytes().get(cursor) == Some(&b'(') -} - -fn identifier_end(python: PythonText, sql: &str, start: usize) -> usize { - let mut cursor = start; - while let Some(character) = char_at(sql, cursor) { - if !is_identifier_continue(python, character) { - break; - } - cursor += character.len_utf8(); - } - cursor -} - -/// Python's `str.isspace` skip. -fn space_end(sql: &str, start: usize) -> usize { - let mut cursor = start; - while let Some(character) = char_at(sql, cursor) { - if !is_python_space(character) { - break; - } - cursor += character.len_utf8(); - } - cursor -} - -fn is_identifier_continue(python: PythonText, character: char) -> bool { - character == '_' || is_python_alnum(python, character) -} - -fn char_at(sql: &str, index: usize) -> Option { - sql.get(index..).and_then(|rest| rest.chars().next()) -} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs index 12ca1c7984..0db21446e8 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs @@ -4,7 +4,9 @@ use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_core::panics::main::native_failure::native_failure; +use sqlbuild_core::text::main::active_python_text::active_python_text; use sqlbuild_core::text::main::python_text::python_text; +use sqlbuild_render::macro_calls::main::macro_call_names::macro_call_names as scan_names; use sqlbuild_render::macro_calls::main::scan_macro_call_sites::scan_macro_call_sites as scan_sites; use sqlbuild_render::macro_calls::main::splice_macro_calls::splice_macro_calls as splice_calls; use sqlbuild_render::macro_calls::models::{MacroCallScan, MacroScanFailure}; @@ -42,6 +44,24 @@ pub(crate) fn scan_macro_call_sites( const MACRO_SCAN_CONTEXT: &str = "native macro call scan"; +/// Python's `find_macro_call_names` (or with `nested`, `find_nested_macro_call_names`): the +/// names, or the message of the `CompileInputError` Python's scan raises. +#[pyfunction] +pub(crate) fn macro_call_names( + py: Python<'_>, + sql: &str, + nested: bool, +) -> PyResult<(Option>, Option<&'static str>)> { + let python = active_python_text(); + let names = py + .compiler_detach(|| Ok(scan_names(python, sql, nested))) + .map_err(compiler_error)?; + Ok(match names { + Ok(names) => (Some(names), None), + Err(error) => (None, Some(error.message())), + }) +} + /// Splice rendered macro outputs over their call sites, returning the SQL and substitution spans. #[pyfunction] pub(crate) fn splice_macro_calls( @@ -60,6 +80,7 @@ pub(crate) fn splice_macro_calls( pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(scan_macro_call_sites, module)?)?; + module.add_function(wrap_pyfunction!(macro_call_names, module)?)?; module.add_function(wrap_pyfunction!(splice_macro_calls, module)?)?; module.add_class::()?; Ok(()) diff --git a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs index 1cf06b2bf1..542caab55e 100644 --- a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs +++ b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs @@ -60,6 +60,23 @@ pub(crate) fn top_level_calls( (calls, None) } +/// Python's `_scan_sqlbuild_call_names(include_nested=True)`: every call start's name in order, +/// continuing just past the character after each name, as Python's cursor does. +pub(crate) fn every_call_name(python: PythonText, text: &str) -> Result, ScanError> { + let mut names: Vec = Vec::new(); + let mut cursor = 0; + while cursor < text.len() { + let Some(start) = next_macro_start(python, text, cursor)? else { + break; + }; + let name_end = identifier_end(python, text, start + 1); + names.push(text[start + 1..name_end].to_owned()); + let open = skip_whitespace(text, name_end); + cursor = open + char_at(text, open).map_or(1, char::len_utf8); + } + Ok(names) +} + /// Names of every call nested in `args` in source order, in one pass without recursion. pub(crate) fn nested_names(python: PythonText, args: &str) -> Result, ScanError> { let bytes: &[u8] = args.as_bytes(); diff --git a/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs b/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs new file mode 100644 index 0000000000..60b604432f --- /dev/null +++ b/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs @@ -0,0 +1,35 @@ +//! Python's `find_macro_call_names` and `find_nested_macro_call_names`. + +use sqlbuild_core::text::models::PythonText; + +use crate::macro_calls::_helpers::scanner::{every_call_name, top_level_calls}; +use crate::macro_calls::models::ScanError; + +/// Python's `_scan_sqlbuild_call_names`: unique call names in encounter order, declaration +/// calls included; top-level calls only, or with `nested` every call start the scan visits; or +/// the error Python's scan raises. +pub fn macro_call_names( + python: PythonText, + sql: &str, + nested: bool, +) -> Result, ScanError> { + if !sql.contains('@') { + return Ok(Vec::new()); + } + let scanned: Vec = if nested { + every_call_name(python, sql)? + } else { + let (calls, failure) = top_level_calls(python, sql); + if let Some(failure) = failure { + return Err(failure.error); + } + calls.into_iter().map(|call| call.name).collect() + }; + let mut names: Vec = Vec::new(); + for name in scanned { + if !names.contains(&name) { + names.push(name); + } + } + Ok(names) +} diff --git a/crates/sqlbuild-render/src/macro_calls/main/mod.rs b/crates/sqlbuild-render/src/macro_calls/main/mod.rs index 2f945c96d5..46e047d83f 100644 --- a/crates/sqlbuild-render/src/macro_calls/main/mod.rs +++ b/crates/sqlbuild-render/src/macro_calls/main/mod.rs @@ -1,2 +1,3 @@ +pub mod macro_call_names; pub mod scan_macro_call_sites; pub mod splice_macro_calls; diff --git a/crates/sqlbuild-render/src/macro_calls/tests.rs b/crates/sqlbuild-render/src/macro_calls/tests.rs index 6340f8ffc3..f84924210d 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests.rs @@ -2,6 +2,8 @@ mod helpers; #[path = "tests/test_macro_call_memo.rs"] mod macro_call_memo; +#[path = "tests/test_macro_call_names.rs"] +mod macro_call_names; #[path = "tests/test_scan_macro_call_sites.rs"] mod scan_macro_call_sites; #[path = "tests/test_splice_macro_calls.rs"] diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs new file mode 100644 index 0000000000..7f4106d418 --- /dev/null +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs @@ -0,0 +1,49 @@ +use crate::macro_calls::main::macro_call_names::macro_call_names; +use crate::macro_calls::models::ScanError; +use crate::macro_calls::tests::helpers::python_312; + +/// `(sql, top-level names, nested names)` as Python's `_scan_sqlbuild_call_names` returned them. +type NamesCase = ( + &'static str, + Result<&'static [&'static str], ScanError>, + Result<&'static [&'static str], ScanError>, +); + +#[test] +fn given_sql_when_listing_macro_call_names_then_python_scan_agrees() { + let test_cases: [NamesCase; 7] = [ + ("@enum(x) @a(1)", Ok(&["enum", "a"]), Ok(&["enum", "a"])), + ("@ab c(1)", Err(ScanError::MissingParenthesis), Ok(&["ab"])), + ("x@\u{f6}re FROM o", Ok(&[]), Ok(&[])), + ( + "@\u{e9}(1) @b(", + Err(ScanError::UnclosedParenthesis), + Ok(&["\u{e9}", "b"]), + ), + ("'@q()' /* @c() */ @d ( 1 ) @@v", Ok(&[]), Ok(&[])), + ("@a(@b(@c()))", Ok(&["a"]), Ok(&["a", "b", "c"])), + ( + "@a(\"x\") 'oops", + Err(ScanError::UnclosedQuote), + Err(ScanError::UnclosedQuote), + ), + ]; + for (sql, top_level, nested) in test_cases { + let owned = |names: Result<&'static [&'static str], ScanError>| { + names.map(|names| { + names + .iter() + .map(|name| (*name).to_owned()) + .collect::>() + }) + }; + assert_eq!( + ( + macro_call_names(python_312(), sql, false), + macro_call_names(python_312(), sql, true) + ), + (owned(top_level), owned(nested)), + "{sql}" + ); + } +} diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index f7b36e11b1..78764d5835 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -326,6 +326,7 @@ def interpolate_sql_batch( def scan_macro_call_sites( sql: str, python_version: tuple[int, int], unicode_version: str ) -> tuple[list[tuple[int, int, str, list[str] | None, bool]], tuple[int | None, str] | None]: ... +def macro_call_names(sql: str, nested: bool, /) -> tuple[list[str] | None, str | None]: ... def splice_macro_calls( sql: str, sites: list[tuple[int, int]], outputs: list[str] ) -> tuple[str, list[tuple[int, int, int, int]]]: ... diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index fe2ffcb39a..3503419b04 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -22,6 +22,7 @@ from sqlbuild.compiler.frontier.exceptions import NativeStageMismatchError from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.macro_bridge._helpers.call_sites import scan_macro_calls, splice_macro_calls from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( ARGUMENT_REFERENCE_EVENT, @@ -77,25 +78,9 @@ def expand_bridged_sql_macros( ] ) if scan.failure is not None or None in call_classes: - unmemoised: list[str] = [ - _evaluated_call_output( - sql=sql, - consumer_path=consumer_path, - state=state, - declarations=declarations, - site=site, - ) - for site in sites - ] - if scan.failure is not None: - _raise_scan_failure( - sql=sql, - consumer_path=consumer_path, - state=state, - declarations=declarations, - failure=scan.failure, - ) - return bridge.splice(sql=sql, sites=sites, outputs=unmemoised) + return _unmemoised_sql_macros( + sql=sql, consumer_path=consumer_path, state=state, declarations=declarations, scan=scan + ) outputs: list[str] = [ _bridged_call_output( sql=sql, @@ -184,6 +169,53 @@ def _bridged_call_output( # noqa: PLR0913 return facts.render_relation_placeholders(macro_result) +def expand_unbridged_sql_macros( + *, sql: str, consumer_path: Path, state: MacroExpansionState +) -> tuple[str, tuple[ExpansionSpan, ...]]: + """Expand one string outside compile inputs: native call sites, each evaluated in order.""" + + if MACRO_TOKEN not in sql: + return sql, () + return _unmemoised_sql_macros( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=_expansion_declarations(state=state, consumer_path=consumer_path), + scan=scan_macro_calls(sql), + ) + + +def _unmemoised_sql_macros( + *, + sql: str, + consumer_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, + scan: MacroCallScan, +) -> tuple[str, tuple[ExpansionSpan, ...]]: + """Evaluate every call in order without the memo, raising where Python's scan raises.""" + + outputs: list[str] = [ + _evaluated_call_output( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=declarations, + site=site, + ) + for site in scan.sites + ] + if scan.failure is not None: + _raise_scan_failure( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=declarations, + failure=scan.failure, + ) + return splice_macro_calls(sql=sql, sites=scan.sites, outputs=outputs) + + def _raise_scan_failure( *, sql: str, diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macros.py b/src/sqlbuild/compiler/compile/_helpers/render/macros.py index c562edff72..3cde582fbc 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macros.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macros.py @@ -16,6 +16,7 @@ from types import MappingProxyType, ModuleType from typing import cast +import sqlbuild._native as _native from sqlbuild.compiler.compile._helpers.explicit_references.macro_arguments import ( call_site_sql_references, reject_macro_generated_references, @@ -28,7 +29,6 @@ from sqlbuild.compiler.compile.constants import ( DECLARATION_REFERENCE_NAMES, MACRO_CONTEXT_PARAMETER_NAME, - MACRO_TOKEN, SQL_OPEN_PAREN_TOKEN, SQL_QUOTE_TOKENS, ) @@ -55,8 +55,6 @@ DiscoveredMacroFile, EnumDeclaration, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( DECLARATION_READ_EVENT, @@ -257,20 +255,6 @@ def load_project_macros(macro_files: tuple[DiscoveredMacroFile, ...]) -> dict[st return loaded_macros -def _inventory_project_macros( - *, macro_files: tuple[DiscoveredMacroFile, ...] -) -> StaticMacroInventory: - """Validate and inventory project macro exports without executing authored code.""" - - try: - analyses: dict[str, _MacroModuleAnalysis] = _analyze_project_macro_modules( - macro_files=macro_files - ) - except CompileInputError: - return _inventory_project_macros_tolerantly(macro_files=macro_files) - return _inventory_from_analyses(macro_files=macro_files, analyses=analyses) - - def _macro_inventory_fault( *, error: CompileInputError, macro_files: tuple[DiscoveredMacroFile, ...] ) -> StaticMacroFault: @@ -1217,24 +1201,8 @@ def expand_sql_macros_result( consumer=consumer, ) bridge: MacroBridge | None = active_macro_bridge() - if bridge is None and MACRO_TOKEN in sql: - report_native_fallback( - site=NativeFallbackSite.MACRO_UNBRIDGED_EXPANSION, kind="outside_compile_inputs" - ) - expanded_sql, spans = ( - _expand_sql_macros( - sql=sql, - consumer_path=file_path, - state=state, - stack=(), - ) - if bridge is None - else _bridged_sql_macros( - sql=sql, - consumer_path=file_path, - state=state, - bridge=bridge, - ) + expanded_sql, spans = _bridged_sql_macros( + sql=sql, consumer_path=file_path, state=state, bridge=bridge ) return MacroExpansionResult( sql=expanded_sql, @@ -1246,33 +1214,32 @@ def expand_sql_macros_result( def _bridged_sql_macros( - *, sql: str, consumer_path: Path, state: MacroExpansionState, bridge: MacroBridge + *, sql: str, consumer_path: Path, state: MacroExpansionState, bridge: MacroBridge | None ) -> tuple[str, tuple[ExpansionSpan, ...]]: from sqlbuild.compiler.compile._helpers.macro_bridge.expansion import ( expand_bridged_sql_macros, + expand_unbridged_sql_macros, ) + if bridge is None: + return expand_unbridged_sql_macros(sql=sql, consumer_path=consumer_path, state=state) return expand_bridged_sql_macros( sql=sql, consumer_path=consumer_path, state=state, bridge=bridge ) -def _expand_sql_macros( - *, - sql: str, - consumer_path: Path, - state: MacroExpansionState, - stack: tuple[DeclarationIdentity, ...], -) -> tuple[str, tuple[ExpansionSpan, ...]]: - if MACRO_TOKEN not in sql: - return sql, () - return _expand_resolved_sql_macros( - sql=sql, - consumer_path=consumer_path, - state=state, - declarations=_expansion_declarations(state=state, consumer_path=consumer_path), - stack=stack, - ) +def _inventory_project_macros( + *, macro_files: tuple[DiscoveredMacroFile, ...] +) -> StaticMacroInventory: + """Validate and inventory project macro exports without executing authored code.""" + + try: + analyses: dict[str, _MacroModuleAnalysis] = _analyze_project_macro_modules( + macro_files=macro_files + ) + except CompileInputError: + return _inventory_project_macros_tolerantly(macro_files=macro_files) + return _inventory_from_analyses(macro_files=macro_files, analyses=analyses) def _expansion_declarations( @@ -1290,62 +1257,13 @@ def _expansion_declarations( return declarations -def _expand_resolved_sql_macros( - *, - sql: str, - consumer_path: Path, - state: MacroExpansionState, - declarations: DeclarationResolutionContext | None, - stack: tuple[DeclarationIdentity, ...], -) -> tuple[str, tuple[ExpansionSpan, ...]]: - rendered_sql_parts: list[str] = [] - spans: list[ExpansionSpan] = [] - output_length: int = 0 - cursor: int = 0 - while cursor < len(sql): - macro_start_index: int | None = _find_next_macro_start(sql=sql, start_index=cursor) - if macro_start_index is None: - rendered_sql_parts.append(sql[cursor:]) - break - leading_literal: str = sql[cursor:macro_start_index] - rendered_sql_parts.append(leading_literal) - output_length += len(leading_literal) - macro_result: object - next_index: int - macro_result, next_index = _evaluate_macro_call( - sql=sql, - call_start_index=macro_start_index, - file_path=consumer_path, - state=state, - declarations=declarations, - stack=stack, - top_level=True, - ) - if not isinstance(macro_result, str): - raise CompileInputError( - f"Macro '@{_parse_macro_name(sql=sql, call_start_index=macro_start_index)}' in " - f"'{consumer_path}' must return a SQL string when used directly in SQL" - ) - macro_result = state.facts.render_relation_placeholders(macro_result) - rendered_sql_parts.append(macro_result) - spans.append( - ExpansionSpan( - source_start=macro_start_index, - source_end=next_index, - output_start=output_length, - output_end=output_length + len(macro_result), - ) - ) - output_length += len(macro_result) - cursor = next_index - return "".join(rendered_sql_parts), tuple(spans) - - def find_macro_call_names(sql: str) -> tuple[str, ...]: """Return unique authored macro call names in encounter order.""" return tuple( - name for name in _find_sqlbuild_call_names(sql) if name not in DECLARATION_REFERENCE_NAMES + name + for name in _native_macro_call_names(sql=sql, nested=False) + if name not in DECLARATION_REFERENCE_NAMES ) @@ -1354,40 +1272,19 @@ def find_nested_macro_call_names(sql: str) -> tuple[str, ...]: return tuple( name - for name in _scan_sqlbuild_call_names(sql=sql, include_nested=True) + for name in _native_macro_call_names(sql=sql, nested=True) if name not in DECLARATION_REFERENCE_NAMES ) -def _find_sqlbuild_call_names(sql: str) -> tuple[str, ...]: - """Return executable SQLBuild call names, excluding quoted and commented text.""" +def _native_macro_call_names(*, sql: str, nested: bool) -> tuple[str, ...]: + """Every call name Python's scan finds, declaration calls included.""" - return _scan_sqlbuild_call_names(sql=sql, include_nested=False) - - -def _scan_sqlbuild_call_names(*, sql: str, include_nested: bool) -> tuple[str, ...]: - if MACRO_TOKEN not in sql: - return () - - names: list[str] = [] - seen: set[str] = set() - cursor: int = 0 - while cursor < len(sql): - macro_start_index: int | None = _find_next_macro_start(sql=sql, start_index=cursor) - if macro_start_index is None: - break - macro_name: str = _parse_macro_name(sql=sql, call_start_index=macro_start_index) - if macro_name not in seen: - seen.add(macro_name) - names.append(macro_name) - opening_paren_index: int = _skip_whitespace( - sql=sql, start_index=macro_start_index + 1 + len(macro_name) - ) - cursor = ( - opening_paren_index + 1 - if include_nested - else _find_matching_paren(sql=sql, opening_paren_index=opening_paren_index) + 1 - ) + names: list[str] | None + message: str | None + names, message = _native.macro_call_names(sql, nested) + if names is None: + raise CompileInputError(message or "") return tuple(names) @@ -1759,7 +1656,7 @@ def _validate_final_macro_sql(*, macro_name: str, file_path: Path, macro_result: "bytes decoded with errors='surrogateescape'" ), ) from None - generated_calls: tuple[str, ...] = _find_sqlbuild_call_names(macro_result) + generated_calls: tuple[str, ...] = _native_macro_call_names(sql=macro_result, nested=False) if not generated_calls: return calls: str = ", ".join(f"@{name}()" for name in generated_calls) diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index f680118343..5438915f3e 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -35,8 +35,6 @@ class NativeFallbackSite(StrEnum): SCOPE_REBIND_LOOKUP = "declaration_scopes.rebind_lookup" LINT_EXPANSION = "model_loop.lint_expansion" - MACRO_UNBRIDGED_EXPANSION = "macro_calls.unbridged_expansion" - MACRO_BRIDGE_UNAVAILABLE = "macro_calls.bridge_unavailable" MACRO_CALL_MOCKED = "macro_calls.mocked_evaluation" PROJECT_ASSEMBLY = "project_assembly.assembly" SQL_TEST_MACRO_MOCK_QUERIES = "sql_test_glue.macro_mock_queries" diff --git a/src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py b/src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py new file mode 100644 index 0000000000..ab305f7354 --- /dev/null +++ b/src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py @@ -0,0 +1,63 @@ +"""Native macro call-site scans and splices of one authored string.""" + +from __future__ import annotations + +import sys +import unicodedata + +import sqlbuild._native as _native +from sqlbuild.compiler.compile.models import ExpansionSpan +from sqlbuild.compiler.macro_bridge.models import MacroCallScan, MacroCallSite, MacroScanFailure + + +def scan_macro_calls( + sql: str, + *, + python_version: tuple[int, int] | None = None, + unicode_version: str | None = None, +) -> MacroCallScan: + """The complete top-level call sites of `sql`, and where its scan raises, if it does.""" + + rows: list[tuple[int, int, str, list[str] | None, bool]] + failure: tuple[int | None, str] | None + rows, failure = _native.scan_macro_call_sites( + sql, + python_version or (sys.version_info[0], sys.version_info[1]), + unicode_version or unicodedata.unidata_version, + ) + return MacroCallScan( + sites=tuple( + MacroCallSite( + start=start, + end=end, + name=name, + tree_names=None if tree_names is None else tuple(tree_names), + typed_reference_text=typed_reference_text, + ) + for start, end, name, tree_names, typed_reference_text in rows + ), + failure=None + if failure is None + else MacroScanFailure(call_start=failure[0], message=failure[1]), + ) + + +def splice_macro_calls( + *, sql: str, sites: tuple[MacroCallSite, ...], outputs: list[str] +) -> tuple[str, tuple[ExpansionSpan, ...]]: + """Replace each call site with its output, returning the SQL and substitution spans.""" + + rendered: str + spans: list[tuple[int, int, int, int]] + rendered, spans = _native.splice_macro_calls( + sql, [(site.start, site.end) for site in sites], outputs + ) + return rendered, tuple( + ExpansionSpan( + source_start=source_start, + source_end=source_end, + output_start=output_start, + output_end=output_end, + ) + for source_start, source_end, output_start, output_end in spans + ) diff --git a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py index 7d91e74c2c..3c09bbe060 100644 --- a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py @@ -14,6 +14,7 @@ LoadedMacro, MacroContext, ) +from sqlbuild.compiler.macro_bridge._helpers.call_sites import scan_macro_calls, splice_macro_calls from sqlbuild.compiler.macro_bridge._helpers.store_environment import ( digest_module_files, module_digests_metadata, @@ -32,7 +33,6 @@ MacroCallClass, MacroCallScan, MacroCallSite, - MacroScanFailure, ModuleSources, ) from sqlbuild.compiler.macro_bridge.types import MacroCallEvent, MacroCallRecord @@ -139,25 +139,8 @@ def _observe_modules(self) -> bool: def scan(self, sql: str) -> MacroCallScan: """The complete top-level call sites of `sql`, and where its scan raises, if it does.""" - rows: list[tuple[int, int, str, list[str] | None, bool]] - failure: tuple[int | None, str] | None - rows, failure = _native.scan_macro_call_sites( - sql, self._python_version, self._unicode_version - ) - return MacroCallScan( - sites=tuple( - MacroCallSite( - start=start, - end=end, - name=name, - tree_names=None if tree_names is None else tuple(tree_names), - typed_reference_text=typed_reference_text, - ) - for start, end, name, tree_names, typed_reference_text in rows - ), - failure=None - if failure is None - else MacroScanFailure(call_start=failure[0], message=failure[1]), + return scan_macro_calls( + sql, python_version=self._python_version, unicode_version=self._unicode_version ) def call_class( @@ -271,19 +254,7 @@ def splice( ) -> tuple[str, tuple[ExpansionSpan, ...]]: """Replace each call site with its output, returning the SQL and substitution spans.""" - rendered: str - spans: list[tuple[int, int, int, int]] - bounds: list[tuple[int, int]] = [(site.start, site.end) for site in sites] - rendered, spans = _native.splice_macro_calls(sql, bounds, outputs) - return rendered, tuple( - ExpansionSpan( - source_start=source_start, - source_end=source_end, - output_start=output_start, - output_end=output_end, - ) - for source_start, source_end, output_start, output_end in spans - ) + return splice_macro_calls(sql=sql, sites=sites, outputs=outputs) def stats(self) -> tuple[int, int, int]: """Memo hits, misses and recorded results so far.""" diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index 3a38731cee..201058d9f5 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -8,9 +8,7 @@ from collections.abc import Callable from contextvars import Token -import sqlbuild._native as _native -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.discovery._helpers.native.payloads import native_text_runtime from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS @@ -19,9 +17,7 @@ def run_with_macro_bridge[T](*, stage: Callable[[], T]) -> T: """Run `stage` once with the bridge; its errors are the Python stage's, raised as they occur.""" python_version: tuple[int, int] = (sys.version_info[0], sys.version_info[1]) - if not _native.native_text_supported(python_version, unicodedata.unidata_version): - report_native_fallback(site=NativeFallbackSite.MACRO_BRIDGE_UNAVAILABLE) - return stage() + _ = native_text_runtime() bridge: MacroBridge = MacroBridge( python_version=python_version, unicode_version=unicodedata.unidata_version ) diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 90f944d542..34b7fc3fca 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -99,10 +99,10 @@ def _perturbed(self, **changes): _record.write(json.dumps({"kind": "legacy_fallback", "site": "orders.sql"}) + "\\n") """ -MACRO_BRIDGE_SABOTAGE: str = """ +PROJECT_ASSEMBLY_SABOTAGE: str = """ import sqlbuild._native as native -native.native_text_supported = lambda *_args: False +native.assemble_project_resource_facts = lambda *_args: (None, "sabotaged") """ MODEL_ANALYSIS_SABOTAGE: str = """ diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 86cc33e235..6a58b0dab2 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -9,8 +9,8 @@ from scripts.compiler_differential.main.differential import run_compiler_differential from tests.e2e.scripts.compiler_differential._test_types import NativeFallbackGateTestCase from tests.e2e.scripts.compiler_differential.helpers import ( - MACRO_BRIDGE_SABOTAGE, MODEL_ANALYSIS_SABOTAGE, + PROJECT_ASSEMBLY_SABOTAGE, harness_arguments, write_native_perturbation, ) @@ -43,13 +43,13 @@ ), ), NativeFallbackGateTestCase( - description="shipped_macro_bridge_sabotaged_to_python_fails", - perturbation=MACRO_BRIDGE_SABOTAGE, + description="shipped_project_assembly_sabotaged_to_python_fails", + perturbation=PROJECT_ASSEMBLY_SABOTAGE, appended_entries="", expected_exit_code=1, expected_lines=( - "Native fallback allow-list: native-preview macro_calls " - "macro_calls.bridge_unavailable deferred (project): ", + "Native fallback allow-list: native-preview project_assembly " + "project_assembly.assembly deferred (project): ", "not on the allow-list; port it or list it with a reason", ), ), diff --git a/tests/goldens/python_oracle/macro_expansion_seeded_fragments.json b/tests/goldens/python_oracle/macro_expansion_seeded_fragments.json new file mode 100644 index 0000000000..0885790fdd --- /dev/null +++ b/tests/goldens/python_oracle/macro_expansion_seeded_fragments.json @@ -0,0 +1,12002 @@ +[ +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"8492f5fe387e297ad4ae5dc7" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"1b41ba12cab83c0008fda003" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1d977e28728afd254797939f" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"9f53c356290a8bf357d82841" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ebdfcc7a63f33420dff4bd19" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"9735456d035ed18ac8c19df7" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"9e1513683e7afcd2f6eb724a" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"e3e7143b3e63312f585ac6e2" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1b328cda8111fab0b5acfe52" +], +[ +"macro_expansion", +"6502c7a0c709c1a02b6e162e" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"bd2092b5301ca29e9c5fb57d" +], +[ +"macro_expansion", +"a5c98870e77055f4a2a3e593" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"5af7940ab8b00a526f7e590a" +], +[ +"macro_expansion", +"97cfd31d6bf3e8831898c775" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ea68a93e87ff00698f2e9da9" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"806838ba1b24f48b920a2f8b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"2e14fa128ea5a6a48f33ad6a" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"476b8d029becdc2995ae4b8a" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"904f44478272ed5db716f5c5" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"cb7d7b30bc4c2b1e440d43ce" +], +[ +"macro_expansion", +"3212231559e8e1ab8d8408bc" +], +[ +"macro_expansion", +"904e1ab6b6adea69bd724ea5" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"bf51cbd252717f3e50590a4d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3e04713305c56ca9c9e92c99" +], +[ +"macro_expansion", +"6a3a2b6f125414f517f0b53f" +], +[ +"macro_expansion", +"0c266313abb6ce7afcf0be4d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"7e57d97967f8de85df429faa" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"2395d4cf572b294a608b7ed9" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"5dcb5549c75f3c0d832bc21b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"34fbbf92cb30fddc889428fc" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"bdbaff3eaa5af483e7548920" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dc7a13d13a5fe049636f0a1f" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"5af7940ab8b00a526f7e590a" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"fcc5f9d933370ac1c60842d0" +], +[ +"macro_expansion", +"f7a839a4af1bb3c0c085f366" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"14ecf6b080f8d1e0e9438a6e" +], +[ +"macro_expansion", +"d106354742d31c732c1b31c0" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c4e30bf5422f1ed262b3d33c" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"cd6103ee256460b6ec6a523d" +], +[ +"macro_expansion", +"1515bf6a54892ce2cdf79b8c" +], +[ +"macro_expansion", +"0efc5b02fb690ab179ecbf58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"80b6e57a5fb4b70359eba3f8" +], +[ +"macro_expansion", +"fd20bffb534e79d0fe7302da" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e98e53e2fcb46a1525144a0d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"e225408c824413460f58751f" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"7fa145786f24350ccc7b9c8c" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"d1c1a73b04f490391d60d629" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"c0fe5bf61fd0f0eeacb501e5" +], +[ +"macro_expansion", +"7bcac40c0857e84c2618ba3a" +], +[ +"macro_expansion", +"ccc0c057269b20aa4167d477" +], +[ +"macro_expansion", +"40cbc6d635d4415aecc6b764" +], +[ +"macro_expansion", +"c6c00ae654a804c64400c8e0" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3b30ba4be263603b3f2421a5" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"288900edf7a27180893bfed4" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"bf7448f6ad7b94b1d4ab4d2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"8e98b1f56eaa480fb3e856db" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"606438a77a147214f7b23530" +], +[ +"macro_expansion", +"c25901dd350bcf008c631d50" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"f33a55b3d100368891337316" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"9e1522340b61f30407de9fba" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"c2f9a5c557e4b2b3cfee2743" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ead253babbb3e0543d2cfce0" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"3adcb2bb552c5d5eb8a0e21e" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"eca3fc7a01658e134595005e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"426de7e3d8e7c788fcf4087d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"a5c5152975733367bacfad4a" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"9d07dc324ae7a0c50338986b" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"cef7c3a54006d9380fc99b36" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"9e7690091f83a24af6485c69" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"e56091c6a6fafec18d106d54" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"5f6d99f98d0a8ceac64c44bb" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"a7a034363ea97fe48fca7dfd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"268d15147a2a6ddfd5f54ec4" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ca141008a95bbef34cae81ef" +], +[ +"macro_expansion", +"d9d6f7586866a8ba2d6cd929" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"e1b20a24241be43a71ffc50c" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"e37f88df43e56c580c0c792a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"00628c5012d22e71eda04a2c" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"d65d609a0a8539407f4d6229" +], +[ +"macro_expansion", +"adb7b7c3b3fdf0b88ae11f46" +], +[ +"macro_expansion", +"ad70ef0520652c786a077df2" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"230ff736623bef0af8321bdb" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"4edddfbc293d38086e0f775a" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ce004a9cc57d9a31acd360f9" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"3951d6f04e3128ead5eae308" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"af4c2dd8de74c30dc9258ef4" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"cae7ed5ea9d501b52ffe5d3b" +], +[ +"macro_expansion", +"aabee86e8e9b92b767a91ade" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"30ea12990cf2bb3eabca5421" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"9bda70785e93c48439cae3d7" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"530373e9240bacfb1911ce91" +], +[ +"macro_expansion", +"7acdf6dd3122748b336fc88d" +], +[ +"macro_expansion", +"8f7759a1ee561a5461f22a17" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"b7d60461f4c405fa8611d54e" +], +[ +"macro_expansion", +"f484e36d98fb308fd8225d8e" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"cf708dee93e8a097ac49c5c3" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"a64f995b374854cddd80419f" +], +[ +"macro_expansion", +"09997a77e24da76e606a9d9b" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"606438a77a147214f7b23530" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"43b4303d27667fc6ba2a53d4" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"9044f28bdaf17b01cc554aee" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"9743438fb82a99f7df805167" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"2372aac9321f9bff66619246" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"4267a558aba31d125a880ca5" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"5af7940ab8b00a526f7e590a" +], +[ +"macro_expansion", +"b950e9fa139763df1b90af42" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"8fb68f98ae0a015ea9168d8e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"b356bbe167977259c9ce4c24" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"315c91b4b6a6d5bfadcc2414" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"48d6b4a81b6f2a8b581acd52" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"041e46557774f89a01db4841" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"d453e99dce3639765307c09c" +], +[ +"macro_expansion", +"65e9e8fb80b2c7ea58207ef9" +], +[ +"macro_expansion", +"1bcc45638e9cd7c315398f71" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"3f5e2f385f932d4edc6311d6" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"efd3d1db961e5325f448c3af" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"794cbb4870baca6077a7dd3b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"5282cc6d3bd1e2bd403db5ac" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"61cede9d44a051c13602fb75" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"202bdf56f293afb18ffce83b" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"9bec430a93ed18b87104b114" +], +[ +"macro_expansion", +"a282bfeeb38c0231588e58c5" +], +[ +"macro_expansion", +"9ce2fdd867deb5a2c7593992" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"e9595e847faf9dcf8bc653ad" +], +[ +"macro_expansion", +"05ae054012fa3356b4ec1b6b" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"6bc422f290e5fcb7528a60fd" +], +[ +"macro_expansion", +"907afea1470f7cc5bfe6901b" +], +[ +"macro_expansion", +"0f82d71af704591cffe51cc2" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"63985dae99cecffe1f8f7893" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c90ff27d5c30f7e91ca0656b" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"e016c6baeaa86ae6b05c1745" +], +[ +"macro_expansion", +"21d81250d8e5cb403e624414" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"90905294a31a7dc3d53a44e2" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"42bff8a9cf4ea9528c256966" +], +[ +"macro_expansion", +"4a34962aee4df522c5fdd0c4" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"5bbb7a5746fb4f9c8ae3c467" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ce5ce3261066331ef4cdcd17" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"30bb5cf1625c3005cff147b8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"c361e6cd5310f78c22612a6d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"e9488cc04bbbbeb305e58073" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"0efc5b02fb690ab179ecbf58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"b44f027debfcfb1868da58fe" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"db9491893ddca90825516860" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"9e363021edc0431c23ac5259" +], +[ +"macro_expansion", +"e5eb667b1cb1abb53d1053d3" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd88b30af8ccb96a87e508a2" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"a67ec40268712e46048979bb" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"6aa36ddb03b106160706a558" +], +[ +"macro_expansion", +"75459f561d93657103660e62" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"96b4a4b376497b3f31051ad9" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"735b08fc31c2d1280efe0975" +], +[ +"macro_expansion", +"91761b49c6da2c0fd26e1c85" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c0fe5bf61fd0f0eeacb501e5" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"d940c9b2c3936faae4de250c" +], +[ +"macro_expansion", +"ec11f1066af204257a0da999" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"f7cec8a592d485155cec9c5f" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"00da12d674b27b036c626c39" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"72f6a690c9d424e58a38161a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"d944663cd44cf64e65a57f92" +], +[ +"macro_expansion", +"50282b149370144e96b77645" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"3951d6f04e3128ead5eae308" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"767b6e01cf542abe2cf83da8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"54301c4eb45c71958baace82" +], +[ +"macro_expansion", +"a6dee33ce3669ce5450f0f2b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"fc9091788cfde12a7ba701fa" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"29d31dda5e3a066f4b51bf81" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"6cdcbd4cce635bcff23a5982" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"13e11a4eee9f0558c33a6c4e" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"0a4edc72f828e832a0729ff5" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"a6e7b71340e150ee8ed2008c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"2d169d881f35e2b418f6bdac" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"f08d26a52d463188498a677e" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"9776ac25855b7168e2f1a4b8" +], +[ +"macro_expansion", +"5d5d4a6ac19fffb6a2f461ae" +], +[ +"macro_expansion", +"d527163e34dc20aeb98939db" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"d686bf0b64732149efa584a1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"9ee2ef94efaf270bd8baeb4d" +], +[ +"macro_expansion", +"4b3d9c0c6339bdbba06cc30a" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"0df1cad86e84826907808ff2" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"7c7b65713012d704bde6a882" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"8a50de3d51346b5af3a098b3" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"12de0ce01a4982b5251b1e9b" +], +[ +"macro_expansion", +"4e248c2e631c26a764a5283c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"66baf65da6caede113cc2a54" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"d27ecc7198b1eded8c1a829e" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1bcc45638e9cd7c315398f71" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"23d8c43f5b697263259ebb8c" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"757d9bb76c0e544011d32efc" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"7bcac40c0857e84c2618ba3a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"d0e47567e5309ed001efd850" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"766215f3d9b934bf1e679165" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"c336b9c71a741425e9db0663" +], +[ +"macro_expansion", +"f6c4e58328dae734df2ca799" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"11ffed324804b32211fa5b11" +], +[ +"macro_expansion", +"4ee887e620ec5304baae938b" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"fe6faf65ec9bc3c6d4c1a6da" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c1b3ce4e9849ee8f8706ea4a" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"00d8424ac205c4e413ca73e0" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"cf0247b1c0b013bf389d4417" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b599681b953bd6433db495" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"39946ca529365c17719f945e" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"5b04379e0c9239b6bacc18ca" +], +[ +"macro_expansion", +"f325c153945afe7d979aaa91" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"613711e7287fe770cbee88da" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"34f95877e0a555ec215c537f" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"0379fb5bf39080ba136b7b19" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ea010e80dbdfe72b774f670a" +], +[ +"macro_expansion", +"aea57db73579ed2af4a301f9" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"6086226146bbfe67934b0258" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"bf36ba15685b129b87c9ee66" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"3e3453bfc58e03f585377140" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"d952d763ccee06d886f27052" +], +[ +"macro_expansion", +"6dfc7809ae3e71c32c74042f" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"63571aa3049006161341c23a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"087d097a346559dc2ded9eb6" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"0397a13bcc997e9785b3811a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"151b8b5f3b1969637c598f66" +], +[ +"macro_expansion", +"81a6407d4a3efd1542e4591b" +], +[ +"macro_expansion", +"01aa87a852ff2fcbc9b379cd" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"d556dc5c94a3229f7d4d8f0c" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"5d933df0e28e49faa145278b" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"d64f4d785182868025b53a4c" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"68c208dc53ccda5635fc0e4a" +], +[ +"macro_expansion", +"bcee600e74d7d69822dd1c1b" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"882c21541c0c1f5bb9f4a920" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"bca7c6f515e3390b1596be86" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"387695025e0631f8721acc6b" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"0dc57fe6f11cf0fa51b7254d" +], +[ +"macro_expansion", +"72e29d14619ebaf708300b73" +], +[ +"macro_expansion", +"2044203bd2540a3d28406696" +], +[ +"macro_expansion", +"1d629067c9a85a8ce906a442" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ddbe6f8646e4cb8fcded9658" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"3235af865dc17cfa5f49fad6" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1019d9e46ca51caab5e360a3" +], +[ +"macro_expansion", +"a75b0f69f3b085c175e6e07c" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"f3d56b53788b0bef1b076c72" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"9ebc028667916f630c2fd425" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"433b1320e40e009b41d0169b" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"adf7b73617a5d1987060f08a" +], +[ +"macro_expansion", +"b2c6063f8d3b8854c9a43c09" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1bcc45638e9cd7c315398f71" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"32da0a04e7f91c16d7d224ea" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"d3ac886fe49076058233540c" +], +[ +"macro_expansion", +"73a340634fb335e07be74c69" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"a957f5c447eea11c0bf77eb0" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"4d0f92be00645b8e47db28a2" +], +[ +"macro_expansion", +"2fd06f96b8e1eb3a1b5ec5c8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"636ebba518f80d5f29958261" +], +[ +"macro_expansion", +"1e5ad78f684d638af97ff4c6" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"43d87665bb5dbef9310e621a" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"16ff947138c04a16088c5ee9" +], +[ +"macro_expansion", +"5897b74c0449d3a4a2dae3e4" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"98c103abcfe4f1bd61ddd37d" +], +[ +"macro_expansion", +"2bd5d2e2b3bf15fff5fa5ac5" +], +[ +"macro_expansion", +"e7bbb38da6e6bece5c29d312" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"3a318527d5a8ef79b3acad0c" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"7bcac40c0857e84c2618ba3a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"43967e9758b7a25a04b9a5cb" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"5e5aa7412202bf4a25971cdc" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"27565b0da62ff1662bd8221c" +], +[ +"macro_expansion", +"71c871bce4b39c8f7211e18f" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"cf89ac3f0e6cc3a4053f8549" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"7c7b65713012d704bde6a882" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"4d1cdac664b2ee1fa8fa0d35" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"31e6dd03225eb7297caececd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"95ee75ddc699238b87261603" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"5eac29f758f45303490884da" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"bb93281f586e1a5e212b2625" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"a78b6c3c26727704d3bdf98b" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"406eefda359b87d654a21b81" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"66a3d87c29f6a7da763c4c86" +], +[ +"macro_expansion", +"610c2c6160a3c1565c39323d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"eb49d93b8b7236da680302a9" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"29cf03922ba1d65aba420297" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"4858450c4bbf8197b55bee96" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"34a3a695b2bf2f34eeac6762" +], +[ +"macro_expansion", +"78e95c23a261f0fbd0da8b71" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"72e787e63126f6c475fcc99d" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c33b697c1dce1f0ea741b635" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"532da9c5be134097e781503e" +], +[ +"macro_expansion", +"2fd06f96b8e1eb3a1b5ec5c8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"6a3a281b4166a6e51d52e8e6" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"339e526db6ef1a3e95cd3797" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"4d93abe9f51870e0795fef0e" +], +[ +"macro_expansion", +"14590c322ba20c645e9fc9fd" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"b7ae6d5039eabf2d9f46d886" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"e4fe72ccb30e6e29eedb0cd3" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"478fb9ebc2d6633ee6f873be" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"644729663b19b456c400455b" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"a03f29f23122d71d8a0ea1b6" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"a37f9e852614334d1406db2b" +], +[ +"macro_expansion", +"112838a70f21bf971162ca3d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c7aca1bde5b37aa3826586eb" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"0efc5b02fb690ab179ecbf58" +], +[ +"macro_expansion", +"d649c46a230a88850c2c597f" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"8a8369c06404ff94937e6ca3" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"b02dcd5ebf1991bc19ecb141" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"eacdf4ea5db402c38b52fa3d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"16f940d0bf1c3e117bbef9f8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"258fccf1c97ef037dab74a86" +], +[ +"macro_expansion", +"2abadf304a71f2231d8ae0d2" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"4ee887e620ec5304baae938b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"3b649768b4abe83d62ff56df" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"8e62dc0ca88856e26701d82d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"de0736cf0d504840cafe2b43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"3398acd17fb9b190a4f2d9f3" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ad10e72968973f4149d54dfd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"7b8b9b2ee0dfefb04809229b" +], +[ +"macro_expansion", +"f242ebca9bedd3e383a01845" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"e290f3ea906df7e037734fba" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"23db4bd63693a9560447c1f0" +], +[ +"macro_expansion", +"84bd5dc79017f77ef2bb22e6" +], +[ +"macro_expansion", +"26285f79acad0374be3d36c8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"de822acdc5dc04706e31246e" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"c086a84c955083cba0a0751a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"849555d449d7c6ff911629df" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"9df95ec2ac0de265ee7ac012" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"9c5838ad06dd051a008654ed" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"0f115d892b2f0f26514a8bf5" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"9f373385134941b8921a8f2e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ff5d2950ff9db93464cec0c8" +], +[ +"macro_expansion", +"8edf6cb5607b23bdbe1b6e17" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"735b08fc31c2d1280efe0975" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"8732ce85f4553b86511a432c" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dac1f00734838fa3881c7436" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"250be9ea3ae07816a2d4160d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"543b1ce26cc6c79fcc591548" +], +[ +"macro_expansion", +"4ee887e620ec5304baae938b" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"4ee887e620ec5304baae938b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ce9920b0a0317f1464d78830" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7fdaad1e670392e2ad1db597" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"bf5bad3bea71a4b1dd3e14e6" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2b6cc6cda06fee90f3435146" +], +[ +"macro_expansion", +"e07f13aa84fb5b885af85a8b" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"b93a3342280f70503b8a8469" +], +[ +"macro_expansion", +"0055252313377ae62a620c69" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"3df2b8aa17bb720ea7762527" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"f151bc2e5824362d79ab715f" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"138e275a602413ffaf9de95a" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1bcc45638e9cd7c315398f71" +], +[ +"macro_expansion", +"bbbee8807e67ec3e6c5bcb89" +], +[ +"macro_expansion", +"e6693e7bcf4339234917c079" +], +[ +"macro_expansion", +"666fe5f7ebca1f93891a7ca1" +], +[ +"macro_expansion", +"9743438fb82a99f7df805167" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"df4aee3ab175d2a88c959922" +], +[ +"macro_expansion", +"a563272efaf8c431c05cce88" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"bc811c5e961e8838e3e1ffc1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"669f3530de2c9270fc3535e3" +], +[ +"macro_expansion", +"fe74b1e5bf2f68bf4f5e85bd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"4d987f8eeb2873cd14c9550c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"6d4bf74e857b3bb3c44e7a88" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"05d13d7f04ad308da56452aa" +], +[ +"macro_expansion", +"349cdee64721f2854ccc6005" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"0eaa441b753a3ca0df83893c" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"5a4640edd1593ad2d6ca54c1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"7261719a85eb5092ad9fe476" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"0d47f3df4eafd08b678da0f0" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c7d10ac2524cb1a6fa0d643d" +], +[ +"macro_expansion", +"6493c305c2da00b0fbc28a71" +], +[ +"macro_expansion", +"38514c09275d323d08251a4d" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"eac0564995737c83726622f3" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3951d6f04e3128ead5eae308" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"975204688cf736f862e7f945" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"00743f79e70fd01297ff6588" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"8dc55647bac833692e333965" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"cfaee72fcbcd0a7d7f29984b" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"19231737737bfd80a4281a0c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"d0aecc46fc3df425c1354b8e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"27abca1ee74aa0693f203ebc" +], +[ +"macro_expansion", +"d6307e35e5376f92eb11bdcf" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"14ac38b50007d4bafc3590e9" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"e809cea57eaaef5cadd306ba" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"a9c5261e9283f88b038fd905" +], +[ +"macro_expansion", +"6f420309cb12642f387739f7" +], +[ +"macro_expansion", +"a859ad5dceaa1db72e7613bb" +], +[ +"macro_expansion", +"41d82d8d6bd7c825e165ecec" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"fd7eb1ad7b15865b2b770e31" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"5a7520160ae82ec9b3955635" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"a91d3dfe6c9c9a595464b585" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"d5d296fd1ea1c044d2c13580" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"c8a73c2a0a7c76182c64b25e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"381da973073ea7c3d8d19d71" +], +[ +"macro_expansion", +"fe6faf65ec9bc3c6d4c1a6da" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"181440fc2852fa01e2cdc802" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e20f741655fa9e1f4f4e30de" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"bad8df050a71f06367788974" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"0548448b8a0f98d9db57dadf" +], +[ +"macro_expansion", +"ff91c7071a4fd7c4097944ec" +], +[ +"macro_expansion", +"3f1801b11fcbbbb2bc2c5382" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"eb6ff5b555cdbff56ea34a4e" +], +[ +"macro_expansion", +"f0b31385bab8a3dbfb29a5e0" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"9ac2cd0b5b4155ec31cdeb0e" +], +[ +"macro_expansion", +"c46518cbe4c4007dc3179017" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"db648a62c7f800e953725973" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"a64556b3e76ee618baf546b0" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"f237480591a7d48db97b3f3a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"d7fdcef38bc9d24c57989803" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"65dfacb21e97ca40c55876d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"c6acfc7f468c1ba2eb471dca" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"87c9142a265b1b9c5a8ac02f" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"70e8ab1e3ac17dc017271d9c" +], +[ +"macro_expansion", +"30bbdef47820b1bfc0c021b0" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"04d0a903a3dbdbb03f03f513" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"adf7b73617a5d1987060f08a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"c3f6bb293307054e98f53c06" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2d27cba3a17204c7441b7dbf" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"d556dc5c94a3229f7d4d8f0c" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"9ca3a8196b47db1f866f8be2" +], +[ +"macro_expansion", +"2307f2e5b07e0f2310d20c0a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7789c815eb1826794f1b6bf9" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"34a9d99ba32f73a693d0c4ac" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"537b1f0f8eafb363031b9862" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"65a805104d05e2b7ef352e64" +], +[ +"macro_expansion", +"c8702ba41c13f97821dfffa2" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"0e56638581cd087e49b6e59a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2d78f4fe8140113336f2f0a9" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"c0310883054b2b64958f1254" +], +[ +"macro_expansion", +"f0acb75e5e509e8ddd200884" +], +[ +"macro_expansion", +"9230083b66fead2b3bff7406" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"d1f9935484c75a11030106aa" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"4550c3058201b6fcd4151bf9" +], +[ +"macro_expansion", +"8397345bd749f67b0034e20a" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"f246b7c71a000dd83fe155e2" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"ec6ab80d54779a396ef4960d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e09c7be51b790fc1fd5e0648" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"36b6375e2241e89a936c0ce4" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"d04beb21a6f06c08af95d3a2" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ad749b5782299ef54e318192" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"d8dbbe954bda38b67a660a16" +], +[ +"macro_expansion", +"50b8dad468bae9538fc6c972" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"22fda6f8abb675dd60f6f4d9" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"87d34dbe3a128787f05a8fb7" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"505e19d74a9b7b1e87dbec4b" +], +[ +"macro_expansion", +"9459aa90a392fb6b3da91501" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"e9a3c2cda95f42b5dbb7887b" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"f49dac27ed6e24cca9e76e6d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"b66a4c321fb61aaca71e23a6" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"ab03b1fbf84be96cd5ba6586" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"ee8ac40262482b1292128fe7" +], +[ +"macro_expansion", +"56fc5dc9cb9c91aa949287c3" +], +[ +"macro_expansion", +"96c726415f44932183c1d3eb" +], +[ +"macro_expansion", +"708163c2d2a34e794fba3f23" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"04da2d68d5c949d16fee7831" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"b4bdbebedb94e92ffbe940fc" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"58b8c6cca568121e9fc3dfde" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"0549fab64641856555cec2ef" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"4c5ff3d596c02ac182847ee2" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"14cd47a33915178e478fca98" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"0744a16007480c5b127d7fef" +], +[ +"macro_expansion", +"28afbcd777e0b8e85ae6e83b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ab069beb29b1048b45d06ca5" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"6f7604a2682ae69e368f24ba" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"fa0df3f596a4f346230f433d" +], +[ +"macro_expansion", +"153cd9587b527d150225536c" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c41157f595fb76cea0f76a16" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"8007aa8f04269b9f9aab0953" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"74fa1a89e5281bf446a53ac1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"e73ef0365857a5f8039024c4" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"a7d91066991331f02271e03c" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"7bcfe657ac8be4dc42287db0" +], +[ +"macro_expansion", +"fa982fa800374cd84e0fabf5" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"e8cf0d83de10936cd912a7d2" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"bb747358561d926311e399ec" +], +[ +"macro_expansion", +"31678d1b96db7108fb4c96fe" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"c6e03b4bce40490b50fe5ea9" +], +[ +"macro_expansion", +"3216b16dfe54254bacb6fffa" +], +[ +"macro_expansion", +"7f174926143f1c0b29b45daf" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"cf678a2a66285cfbcf783827" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"cb475022f139466fb6e8f47c" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"e30dbdd55aba068730502480" +], +[ +"macro_expansion", +"3f1cafeb89ec76e6a0400412" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2bd03a3362e9cac96dd0690d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c796f22f6b407c2faf93f4c3" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2af83e772005de18b7ba7982" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"56840d920cfd040cde42282d" +], +[ +"macro_expansion", +"ab03b1fbf84be96cd5ba6586" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"91d0efa04f5eb2372beb8a3a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92ba4cd3f6a685df708c8e4b" +], +[ +"macro_expansion", +"745d473f3ed85a4a92502aa3" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"6d2685ca9e6bc22e6b790497" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"29fc33f4129f46edd947b09e" +], +[ +"macro_expansion", +"f92b54db9d030238b610128c" +], +[ +"macro_expansion", +"995afbc74b727ba9eac8b38a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"3ce696cdfd84a1df26209a20" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"596c63429642c6299df023c7" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"6603fc6c4e0c6c5fc98171a2" +], +[ +"macro_expansion", +"4c1312f7546296273520e7f5" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"766215f3d9b934bf1e679165" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e67a3a23038cc8c7303f8a39" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"b63075bed3ff08308cc8f396" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"6e8a2d86c99dc15372172cad" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"493fcf8f424e072422a37edb" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"cb45ba71891316da00ec2634" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"adc75266412502cf10a392fa" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"acc4ec17882f296486c7e9f7" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"4538950d3b7c71d733b6b40b" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"c3396094b46dfbfca6713f71" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"95def6b19a55ac2fb3bd01b1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffe2157e418fc118e559fc78" +], +[ +"macro_expansion", +"4e34059d10f9b87d7c3deb78" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"766215f3d9b934bf1e679165" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2d069b17a6d425091b1bddab" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"fb9d3555231be734c49749f5" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"313c42fe5b24af8225a754df" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"d28ce918ebecdf9464ca87a1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"f25fb126e7a4adcab3dc11a1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1af19be251280ffe407b322c" +], +[ +"macro_expansion", +"1dc9207aefd2b4be6b224007" +], +[ +"macro_expansion", +"25a667fb1bb37a750bd958da" +], +[ +"macro_expansion", +"758f83b95bad59c14ecef42d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ebb78a63bced258d61484a48" +], +[ +"macro_expansion", +"d7ab80dc7ffef128375dbf12" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8639e91e97492e81db9d0380" +], +[ +"macro_expansion", +"18e40a106446b22fa2abfd22" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"0e10d1591e0ea53582b8f9b0" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"a50785b9a8c65dc0341aa076" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ecb57fc615749aed99172a04" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"80c6db09a5598755fdfae58e" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"4ec5893b37b14fd30f991abc" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"cbb141993cb3bccf9def83a7" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8037fa44692d1eed510ea768" +], +[ +"macro_expansion", +"f50e25828a82c45f2343b227" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c6aaabbc9280b9e469ed80a0" +], +[ +"macro_expansion", +"44abb6aa51d53866c583442c" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8b7fe3beb76ab74ec2fa14a2" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"cacdfd52efbc506797837a67" +], +[ +"macro_expansion", +"d29b77fab08ba75b3a2ad1d9" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"81c1f8f616ec4254a66bb251" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2e5ba9a82bcc78b5edd50ba8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"066cc151ca00b76272d9ed3e" +], +[ +"macro_expansion", +"f7e618dc56eb5f62166c627b" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2de7e05291001cf6f82820e8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"45b092eb05e9998555eca874" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"3a318527d5a8ef79b3acad0c" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"7cf5985adf472ff3b0f7e064" +], +[ +"macro_expansion", +"57ced0758458bb9c815278f3" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"91448bc85ff5bf37da4e2ddb" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"735b08fc31c2d1280efe0975" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"708163c2d2a34e794fba3f23" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"0efc5b02fb690ab179ecbf58" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3951d6f04e3128ead5eae308" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"a7ca01bea83e83970857048f" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"2b44cfb903eccff556fa0231" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"1290db62a86f5196c70db736" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"a097b5e93e20fc520b926be1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7bcfe657ac8be4dc42287db0" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"5f4b41f6c2f154e8cfb5c26f" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"a577476dca83c194229d517b" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"344c343ab950c3b98cb6d972" +], +[ +"macro_expansion", +"5ac75025966e75abfd12d6a9" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"d6b4103375ad6ea870edfa1e" +], +[ +"macro_expansion", +"25f366f42199dd40c0766d10" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"f41f6772410d7f36bf97789a" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8882bae993871070ad84c423" +], +[ +"macro_expansion", +"70ad91a0d9e6491d32ea726e" +], +[ +"macro_expansion", +"37ae90f053003a66661aa613" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"6b823ffd6cb176dd32f41977" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"eedd92f6e33d1c9cc7838b0c" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"e131d98a9612df4edf0b3fc7" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"8b63f1609cafbab226b3af4f" +], +[ +"macro_expansion", +"c2f5a04352ba7270126b51e5" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"a270a6019369cf9d4e56d176" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8b9371c390920b87e859fae8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c20bf40e32939f0a37e8f235" +], +[ +"macro_expansion", +"0efc5b02fb690ab179ecbf58" +], +[ +"macro_expansion", +"bd58a8e4cd096652f40ef732" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"0a8c0adbe729b5509f0c3a11" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"066f9f43296b1b2b28d782a9" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"744e5ebcb3aff2861135b53c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"5dabbb70b40f259a0822f923" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"914587ed7b3250e62e270400" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"bf1fd53a9d64160540ce9c29" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"e89288bbb924140884103452" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"c4ac13b2ed3448e47247bf51" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"9c2bc7cc62c666d9310f5ba6" +], +[ +"macro_expansion", +"d07386a1e5853201a3540a62" +], +[ +"macro_expansion", +"fed4b9630c80355e45c265be" +], +[ +"macro_expansion", +"09a088163d78ecda97033052" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"b804390cca1ab409b4c031e5" +], +[ +"macro_expansion", +"1a0710757a3fb2096f8bcf2d" +], +[ +"macro_expansion", +"bbed1c8f456d2d2ec0bc9f80" +], +[ +"macro_expansion", +"fa72da5603764a4bf3082971" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"b233f3ff5557cec77d5df249" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2036ec4101a7cc976cb4af2b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"387752d70fb7cbc7ce7db350" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"7c7b65713012d704bde6a882" +], +[ +"macro_expansion", +"d556dc5c94a3229f7d4d8f0c" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"766215f3d9b934bf1e679165" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"4a9a55b14f46d8461e4c4832" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"880b3b548c60ab8b15406d09" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2e746ddbb634a85c18e816c0" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c96d42cd2386a2d8c0b5a089" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"519255c2087d8c6708f79b16" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"aa7325c877fa94e46cd8931f" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"21857229d71e36d71e7b1bc3" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7ddaf007022badc73e3c6e2f" +], +[ +"macro_expansion", +"b8ad7c906f6ac9f1a15946b2" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"d8e0594005d7f0906aefa669" +], +[ +"macro_expansion", +"b9ac8861ec1a0f78edb38129" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"b706c171d854c0878ee5f484" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"9f21f08f876e2cc418e3a4f4" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"2b370695d470b91d4019e3d5" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"0c230469a45e34eec2879b8c" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"00743f79e70fd01297ff6588" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"c0fe5bf61fd0f0eeacb501e5" +], +[ +"macro_expansion", +"39f96360548d052d01ec8fa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c899f58121894b2bb80cc431" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"c4ac13b2ed3448e47247bf51" +], +[ +"macro_expansion", +"77bbc7f31496aea3a9c2b1d6" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1976b6ce0075f4829eac7ade" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"b25636ebbf0e1830abdeeca8" +], +[ +"macro_expansion", +"1b42bb9b4f36e7c6bd990aca" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"8850168eaae50b974ebb7a64" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"12128364177af8a9bdce437b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"8db6bbe77a8e14d45881eb67" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"4fa1dae22197bde7ba4d60bf" +], +[ +"macro_expansion", +"ad933b77748af43e2bc7e80c" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"e1998760a5347544a55123b2" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"8cf3ec4830de80e5c8ee30aa" +], +[ +"macro_expansion", +"d2083ad25bae0a748155e094" +], +[ +"macro_expansion", +"ff3562cb938baeccd8039b40" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"20c65634487dc803237c4acf" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"4738fac9196a87f01bf9010c" +], +[ +"macro_expansion", +"1e8183f68adf538b0180c605" +], +[ +"macro_expansion", +"72a7856a8dd186a82cb9b520" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"c1fac7cbf25354e4399e3f4e" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"71caaaac9301c8b902ca3780" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70ded8434da4e9bd19af76b6" +], +[ +"macro_expansion", +"b5a89cc16f59a47768bf3427" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"7ae3476d2be61f9a83e0f922" +], +[ +"macro_expansion", +"adf7b73617a5d1987060f08a" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"adf7b73617a5d1987060f08a" +], +[ +"macro_expansion", +"86a7f614de860ea788eb7c56" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"4ee887e620ec5304baae938b" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"a9148d91ac2e665279c7c0e0" +], +[ +"macro_expansion", +"bac2a2c6ef3b17fd35f15b87" +], +[ +"macro_expansion", +"af2ecf9f087e54beedeb3efb" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"e97f86b46776ced7f3ca46c0" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"0ce7db57c7405f7fd9a15732" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"766215f3d9b934bf1e679165" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"a9f6965e2512ba90b24e8f65" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"50cce66403a054fa2654413a" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ef70f41a57ccba12f7375d9f" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e81e229c44884a9ccb7f5d94" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"41456a90e056f4aaf5e0911d" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"5ccdce86c1ed7c5c277ca64d" +], +[ +"macro_expansion", +"aa4b4485077e90ba5e9c8324" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"735b08fc31c2d1280efe0975" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"2cf111f5cbbd1d08bc2d680d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"8705603e6f5b44c3dda62eb0" +], +[ +"macro_expansion", +"a5ac5fdea8afa6b3b4e72b63" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"28eb402bf54d3451b17edd07" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"53e56a7403823277db61c8ef" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"7828f85a1ea610377560a7b4" +], +[ +"macro_expansion", +"40ecfe9e3f42575feb463f70" +], +[ +"macro_expansion", +"1bcc45638e9cd7c315398f71" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"6c04a4931640af6c62eb2fda" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"85588074e74837d186fbde4d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"146079cc71b242d162abe6ba" +], +[ +"macro_expansion", +"a54102a11027175e3f4d3dd1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"aad199d3b7ccdcc9c43c67fa" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"f1953d4d42c374eb2cc0a226" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"a9ff75fcd2601600ad8bfdf7" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"808ce9d6eb59cd3baa6f448f" +], +[ +"macro_expansion", +"0615e4fdd3c5135a2e3f3db1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"18224741933d84735e8c3345" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"7df52d1231c5454d77579169" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"bedadf5ef3c77649b2ad3d3b" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"42ebfbc1551ec293d91d8ebd" +], +[ +"macro_expansion", +"e13084549a40032a2c756aa1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7ef6feee59b86d669b5dfa4e" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"049a70c66a1dffaa8aff4e0b" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"18b7998b302ed083f943c5a4" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"a7a82142610ac883c6513e9b" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"c0fe5bf61fd0f0eeacb501e5" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"f3a186535d099608c832e2d8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3854350382796f2dcb3a5ef9" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"2d2be85d7b705ee69520f033" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"45f916243d6b5c85d1c1eb90" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"73aec4bca23b2f11791f3809" +], +[ +"macro_expansion", +"6dbba6958f7989e6d287cce6" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"e5e06a040d302f30a06c75cd" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"847066ca718b1fef56dbff64" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"87375f34cf5985036a42b98b" +], +[ +"macro_expansion", +"bfaadea0fc474bf6f954c9d3" +], +[ +"macro_expansion", +"adf7b73617a5d1987060f08a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"d4e589adf0aeff647b5ffb96" +], +[ +"macro_expansion", +"96f3d99ff7407f5e50cf8226" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"4062cb6f81dd97301652f3ae" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"641accd0e6a36b4a712b828f" +], +[ +"macro_expansion", +"cace2f3ae0d36fe09d93f30a" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"333085a1d4f7733d849e1615" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"3558585e300e9f78d1d98610" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"8c9c8288479c03bb26b53989" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"282e879db9f3821d8d8bad9d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"df054836981181079fdb1e23" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"8487af99c90d0cff829dc1e4" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"c0fe5bf61fd0f0eeacb501e5" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"573323ede4bab77ff1f48380" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"c73272cf0c295690bbb32e54" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"4efd59cfcc27d0185bef5492" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"227554d7184c4c824d7d9d85" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"7fa145786f24350ccc7b9c8c" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"3649f6fe51ee9bf5bc61388e" +], +[ +"macro_expansion", +"8a53bfbad06c12ac937c5455" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"f223e62fd2bd28e21122dfb6" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"c0313345b5784b21baaf8b50" +], +[ +"macro_expansion", +"b040ddd442aa425a60f490cd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"08867085ba3bc8ceab44c3e9" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"9a4e18e87760ade7db04ce4a" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"1a54bb06fe13478f03561dca" +], +[ +"macro_expansion", +"91bc0a83436abc5379745ef1" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"9320c79605d5f8f20f4f3900" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"80212d7b41eaf9dfb155e60d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"447ca12d238dd43d6cf1d95f" +], +[ +"macro_expansion", +"710bdf26b1de9dacfff88df7" +], +[ +"macro_expansion", +"2bb4206306d572449528a7c2" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ea6b40f2657cb4771447efdd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"c165f08eb88e6d00006e05d9" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"735b08fc31c2d1280efe0975" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"25671f6b231d223979810296" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"95dfecf96aba4125cf28623e" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"fc37a701e2d3d4279097b671" +], +[ +"macro_expansion", +"fa9e39332847981e30b5cca4" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"e7c0fe9380a0201611fa5c6b" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"7c78d92c5e142131169759d6" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"fa3835aa2ea2380630dac4df" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"b240dfdfbf0045237b40cd1d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"44b3c3adb885402b7f69188e" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"b881f2bf0e0305083986d059" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"5e89dcfcddfc6ce859dbd9c4" +], +[ +"macro_expansion", +"006a2a36ae299628bf10134f" +], +[ +"macro_expansion", +"5432b9ee174fc51fcf1dcc76" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"57853e2b33d99e83608fe8db" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"12e6c97b73d702dfa251a0d4" +], +[ +"macro_expansion", +"16ff947138c04a16088c5ee9" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"dd1d161e1c86ac30d9a698fd" +], +[ +"macro_expansion", +"46a3271f4ff67656bf00cbf1" +], +[ +"macro_expansion", +"90b9145be8158b3c3756dad9" +], +[ +"macro_expansion", +"3df0f02d5862f8e508ca1b8f" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"4240447b25b4c3e66cb9d165" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"6edcc18c34141bab7c1fba33" +], +[ +"macro_expansion", +"f1f833fc0def352bfaed9e4c" +], +[ +"macro_expansion", +"fea4e2213e8e44ce19e18c73" +], +[ +"macro_expansion", +"ace5e876fb1ebaa1aa0bb767" +], +[ +"macro_expansion", +"1a7a4ebf8e788582eea12c43" +], +[ +"macro_expansion", +"70cefe52ce63334596757a57" +], +[ +"macro_expansion", +"5aac3de3214439ab08b7af7a" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py index 678f8f8ce9..139b19a344 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py @@ -32,7 +32,8 @@ class MacroBridgeFailureTestCase: @dataclass(frozen=True) class MacroExpansionDifferentialTestCase: - """Seeded random SQL text that must expand identically with and without the bridge.""" + """Seeded random SQL text that must expand, with and without the bridge, as Python's + expansion recorded in its golden did.""" description: str seed: int diff --git a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py index 270ec0d091..23aa659844 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py +++ b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py @@ -30,7 +30,13 @@ render_error, write_project, ) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.golden_views import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) _FAILURE_BASE_FILES: dict[str, str] = { "sqlbuild_project.toml": MACRO_BRIDGE_PROJECT_FILES["sqlbuild_project.toml"], @@ -205,25 +211,15 @@ def test_given_random_macro_sql_when_expanding_through_bridge_then_matches_pytho python_version=(sys.version_info[0], sys.version_info[1]), unicode_version=unicodedata.unidata_version, ) - - python_outcomes: list[tuple[object, ...]] = [ - expansion_outcome(sql=sql, bridge=None) for sql in texts - ] - bridged_outcomes: list[tuple[object, ...]] = [ - expansion_outcome(sql=sql, bridge=bridge) for sql in texts - ] + unbridged: list[tuple[object, ...]] = [expansion_outcome(sql=sql, bridge=None) for sql in texts] + bridged: list[tuple[object, ...]] = [expansion_outcome(sql=sql, bridge=bridge) for sql in texts] + expected: list[GoldenEntry] = read_golden(golden_name("macro_expansion", test_case.description)) assert ( - mismatches( - inputs=list[object](texts), - expected=list[object](python_outcomes), - actual=list[object](bridged_outcomes), - ) - == test_case.expected_mismatches - ) - assert sum(len(outcome) == 5 for outcome in python_outcomes) >= ( - test_case.expected_minimum_successes - ) + golden_differences(expected, [golden_entry("macro_expansion", item) for item in bridged]), + golden_differences(expected, [golden_entry("macro_expansion", item) for item in unbridged]), + ) == (test_case.expected_mismatches, test_case.expected_mismatches) + assert sum(len(outcome) == 5 for outcome in bridged) >= test_case.expected_minimum_successes assert bridge.stats()[0] > 0 From ce9f3ca4bb1f852a34e8010b1ba711f38f8a00e3 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 20:58:55 +0100 Subject: [PATCH 19/36] feat(native): keep typed output columns and dynamic proofs on the finished analysis session The finished session keeps each model's output columns with their types and nullability, not just names, and each model's dynamic pivot proof from the session's own relation facts, so native consumers read them without re-analysis. --- .../analysis_session/_helpers/session.rs | 21 ++++---- .../main/finished_dynamic_proof.rs | 11 ++++ .../main/finished_model_facts.rs | 2 +- .../src/assembly/analysis_session/main/mod.rs | 1 + .../src/assembly/analysis_session/models.rs | 7 ++- .../analysis_session/tests/helpers.rs | 42 ++++++++++++++- .../tests/test_analysis_session.rs | 51 +++++++++++++++---- .../_helpers/semantic_checks/completion.rs | 6 ++- .../_helpers/semantic_checks/type_recovery.rs | 6 ++- 9 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 243cf4877b..0ff17df9c3 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -31,8 +31,8 @@ use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; use crate::assembly::analysis_session::_helpers::publication::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::models::{ - AnalysisSession, ColumnFact, FinishedSession, LineageFacts, LineageRow, ModelAnalysis, - ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, SessionModelFacts, + AnalysisSession, ColumnFact, ContractProof, FinishedSession, LineageFacts, LineageRow, + ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -125,10 +125,15 @@ impl AnalysisSession { let (schema_additions, analysis_names) = self.catalog.into_changes(); let request: SessionRequest = self.request; let mut model_facts: HashMap = HashMap::new(); - for (model, outcome) in request.models.iter().zip(&models) { + let mut dynamic_proofs: HashMap = HashMap::new(); + for ((model, outcome), proof) in request.models.iter().zip(&models).zip(&dynamic_contracts) + { if let Some(facts) = session_model_facts(&outcome.analysis) { model_facts.insert(model.name.clone(), facts); } + if let PivotOutcome::Proof(proof) = proof { + dynamic_proofs.insert(model.name.clone(), proof.clone()); + } } let finished = FinishedSession { tables: PivotTables { @@ -140,6 +145,7 @@ impl AnalysisSession { }, pool, models: model_facts, + dynamic_proofs, }; let outcome = SessionOutcome { models, @@ -729,19 +735,16 @@ fn enriched_analysis( merged } -/// A compiled model's output names and lineage from a successful analysis with native lineage. +/// A compiled model's typed output columns and lineage from a successful analysis with native +/// lineage. fn session_model_facts(analysis: &ModelAnalysis) -> Option { let (LineageFacts::Native(rows) | LineageFacts::NativeFacts(rows)) = &analysis.lineage; analysis.analysis_succeeded.then(|| SessionModelFacts { - columns: analysis.columns.as_deref().map(column_names), + columns: analysis.columns.clone(), lineage: rows.iter().map(output_sources).collect(), }) } -fn column_names(columns: &[ColumnFact]) -> Vec { - columns.iter().map(|column| column.name.clone()).collect() -} - /// One lineage row as `(output column, [(resource name, column name)])`. fn output_sources(row: &LineageRow) -> (String, Vec<(String, String)>) { let mut sources: Vec<(String, String)> = Vec::with_capacity(row.sources.len()); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs new file mode 100644 index 0000000000..639fb24a1c --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs @@ -0,0 +1,11 @@ +//! The dynamic pivot proof a finished analysis session kept for one model. + +use crate::assembly::analysis_session::models::{ContractProof, FinishedSession}; + +/// The named model's proof from the session's relation facts, or None where it declares none. +pub fn finished_dynamic_proof<'a>( + session: &'a FinishedSession, + name: &str, +) -> Option<&'a ContractProof> { + session.dynamic_proofs.get(name) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs index bac994679b..0d42f7e923 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs @@ -1,4 +1,4 @@ -//! The output names and lineage a finished analysis session kept for compiled models. +//! The typed output columns and lineage a finished analysis session kept for compiled models. use crate::assembly::analysis_session::models::{FinishedSession, SessionModelFacts}; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 3abe4d2a9f..bc38ac75e7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -1,6 +1,7 @@ pub mod attach_analysis_cache; pub mod expression_shapes; pub mod finish_analysis_session; +pub mod finished_dynamic_proof; pub mod finished_fact_models; pub mod finished_model_facts; pub mod prove_dynamic_contracts; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index e99780f2dc..873c9d2371 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -96,12 +96,15 @@ pub struct FinishedSession { pub(crate) tables: PivotTables, pub(crate) pool: Result, String>, pub(crate) models: HashMap, + /// Each model's dynamic pivot proof from the session's own relation facts, by model name. + pub(crate) dynamic_proofs: HashMap, } -/// A compiled model's output names and lineage from an analysis the session completed natively. +/// A compiled model's typed output columns and lineage from an analysis the session completed +/// natively. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SessionModelFacts { - pub columns: Option>, + pub columns: Option>, pub lineage: OutputSources, } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index cdc93a42c7..07c399b61a 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -10,6 +10,7 @@ use sqlbuild_cache::store::models::NativeStore; use crate::assembly::analysis_session::main::attach_analysis_cache::attach_analysis_cache; use crate::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; +use crate::assembly::analysis_session::main::finished_dynamic_proof::finished_dynamic_proof; use crate::assembly::analysis_session::main::finished_fact_models::finished_fact_models; use crate::assembly::analysis_session::main::finished_model_facts::finished_model_facts; use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_session; @@ -276,7 +277,45 @@ fn fact_line(facts: &SessionModelFacts) -> String { .iter() .map(|(output, sources)| format!("{output}<-{sources:?}")) .collect(); - format!("{:?} {}", facts.columns, lineage.join(" ")) + let columns: Option> = facts + .columns + .as_ref() + .map(|columns| columns.iter().map(column_line).collect()); + format!("{columns:?} {}", lineage.join(" ")) +} + +/// The proofs a finished session keeps for `pivot_request`'s models, analysed in one session. +pub(crate) fn kept_dynamic_proofs( + dialect: &str, + sql: &str, + family: Option<(&str, &str, &str)>, +) -> Vec> { + let pivots: PivotBatchRequest = pivot_request(dialect, sql, family); + let names: [&str; 2] = ["customer_amounts", "plain"]; + let models: Vec = names + .iter() + .zip(pivots.models) + .map(|(name, pivot)| ModelRequest { + pivot_sql: pivot.sql.clone(), + dynamic_families: pivot.families, + ..model(name, &pivot.sql, &["raw_orders"], &[]) + }) + .collect(); + let tables: PivotTables = pivots.tables; + let request = SessionRequest { + dialect: tables.dialect, + column_types: tables.column_types.clone(), + column_nullability: tables.column_nullability, + complete_schemas: tables.authoritative_types, + catalog_schemas: tables.column_types, + dynamic_families_by_table: tables.families_by_table, + ..orders_request(models) + }; + let (_, _, finished) = completed(started(request)); + names + .iter() + .map(|name| finished_dynamic_proof(&finished, name).cloned()) + .collect() } /// Proofs over typed `raw_orders` and pivot `status_amounts`: the model, then a family-less one. @@ -331,6 +370,7 @@ pub(crate) fn finished_session(tables: PivotTables) -> FinishedSession { tables, pool: catalog("duckdb", &Vec::new()).analysis_pool(), models: HashMap::new(), + dynamic_proofs: HashMap::new(), } } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index b66ef69372..12c587ab81 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -4,8 +4,9 @@ use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_ use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ - catalog, expected_facts, fact_lines, failed, finished_session, legacy_lines, model_requests, - orders_request, pivot_request, proven, recovered_facts, session_lines, + catalog, expected_facts, fact_lines, failed, finished_session, kept_dynamic_proofs, + legacy_lines, model_requests, orders_request, pivot_request, proven, recovered_facts, + session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ CteRecoveryTestCase, LegacyAnalysisTestCase, ModelSpec, PivotTestCase, SessionFactsTestCase, @@ -177,7 +178,7 @@ fn given_models_when_running_the_session_then_publishes_as_python_does() { fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_successes() { let test_cases = [ SessionFactsTestCase { - description: "native lineage keeps output names and sources", + description: "native lineage keeps typed output columns and sources", models: &[ ( "stg_orders", @@ -193,14 +194,14 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe ), ], expected_facts: &[ - "Some([\"order_id\", \"doubled\"]) \ + "Some([\"order_id INTEGER non_null\", \"doubled DOUBLE unknown\"]) \ order_id<-[(\"raw_orders\", \"order_id\")] \ doubled<-[(\"raw_orders\", \"amount\")]", - "Some([\"missing_column\"]) missing_column<-[(\"raw_orders\", \"missing_column\")]", + "Some([\"missing_column - unknown\"]) missing_column<-[(\"raw_orders\", \"missing_column\")]", ], }, SessionFactsTestCase { - description: "a native legacy analysis keeps its output names and sources", + description: "a native legacy analysis keeps its typed output columns and sources", models: &[ ( "events", @@ -216,8 +217,8 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe ), ], expected_facts: &[ - "Some([\"event_id\"]) event_id<-[(\"raw_events\", \"event_id\")]", - "Some([\"event_id\", \"one\"]) event_id<-[(\"events\", \"event_id\")] one<-[]", + "Some([\"event_id - unknown\"]) event_id<-[(\"raw_events\", \"event_id\")]", + "Some([\"event_id - unknown\", \"one INT non_null\"]) event_id<-[(\"events\", \"event_id\")] one<-[]", ], }, SessionFactsTestCase { @@ -238,8 +239,8 @@ fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_succe ), ], expected_facts: &[ - "Some([\"gr\u{f6}\u{df}e\"]) gr\u{f6}\u{df}e<-[(\"raw_events\", \"event_id\")]", - "Some([\"gr\u{f6}\u{df}e\"]) gr\u{f6}\u{df}e<-[(\"events\", \"gr\u{f6}\u{df}e\")]", + "Some([\"gr\u{f6}\u{df}e - unknown\"]) gr\u{f6}\u{df}e<-[(\"raw_events\", \"event_id\")]", + "Some([\"gr\u{f6}\u{df}e - non_null\"]) gr\u{f6}\u{df}e<-[(\"events\", \"gr\u{f6}\u{df}e\")]", ], }, ]; @@ -639,3 +640,33 @@ fn given_handed_back_models_when_analysing_legacy_then_matches_python_or_defers( ); } } + +#[test] +fn given_a_finished_session_when_reading_dynamic_proofs_then_keeps_each_models_proof() { + let test_cases = [PivotTestCase { + description: "a bare DuckDB pivot keeps its proof; a family-less model keeps none", + dialect: "duckdb", + sql: "PIVOT __source(\"raw_orders\") ON status USING MAX(amount) GROUP BY customer_id", + family: Some(("status", "amount", "MAX")), + expected_outcome: proven( + &[("customer_id", "INTEGER", "non_null")], + Some("DOUBLE"), + "raw_orders", + true, + ), + }]; + for test_case in test_cases { + let PivotOutcome::Proof(expected) = test_case.expected_outcome else { + panic!("{}: the case expects a proof", test_case.description); + }; + + let kept = kept_dynamic_proofs(test_case.dialect, test_case.sql, test_case.family); + + assert_eq!( + kept, + vec![Some(expected), None], + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs index c853ed9954..43ea7c10a7 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs @@ -145,8 +145,10 @@ fn completion_model( Some(names) => names, None => session_model_facts(session, &name)? .columns - .clone() - .unwrap_or_default(), + .iter() + .flatten() + .map(|column| column.name.clone()) + .collect(), }; let lineage: Vec = match lineage { Some(lineage) => lineage, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index 82882184d4..ade9cdca33 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -141,7 +141,11 @@ fn recovery_model( Some(lineage) => (inferred_columns, lineage), None => { let facts = session_model_facts(session, &name)?; - (facts.columns.clone(), facts.lineage.clone()) + let names: Option> = facts + .columns + .as_ref() + .map(|columns| columns.iter().map(|column| column.name.clone()).collect()); + (names, facts.lineage.clone()) } }; Ok(RecoveryModel { From 44cd32aac0ec57f719b3cd4b10614b1a39bf56e4 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:35:57 +0100 Subject: [PATCH 20/36] refactor(native): bring the lane's native ports in line with the structure checks Doc comments are single summary lines; entry modules expose one function with helpers, errors and constants in their roles; placeholder defaults live in sqlbuild-sqltext; fallible probes match their errors. Test helpers move out of test modules and carry no conditional control flow, the golden comparison moves into the compiler test helpers, and the analysis-code scan reads the contract codes from the native crate. Two rule exceptions record the process-wide Python text semantics and the nullability callback's manual Debug. --- .../_helpers/catalog_state.rs | 76 ++++++- .../analysis_session/_helpers/cte_facts.rs | 6 +- .../analysis_session/_helpers/dict_walk.rs | 5 +- .../_helpers/dynamic_pivot.rs | 6 +- .../analysis_session/_helpers/enrichment.rs | 9 +- .../assembly/analysis_session/_helpers/mod.rs | 2 +- .../analysis_session/_helpers/publication.rs | 80 -------- .../_helpers/query_columns.rs | 172 ++++++++++++++++ .../analysis_session/_helpers/session.rs | 13 +- .../main/expression_shapes.rs | 2 +- .../main/finished_dynamic_proof.rs | 11 - .../src/assembly/analysis_session/main/mod.rs | 1 - .../analysis_session/main/query_columns.rs | 175 +--------------- .../main/start_analysis_session.rs | 6 +- .../src/assembly/analysis_session/models.rs | 22 +- .../analysis_session/tests/helpers.rs | 11 +- .../tests/test_analysis_session.rs | 6 +- .../src/assembly/analysis_session/types.rs | 16 +- .../src/assembly/project/_helpers/syntax.rs | 50 +---- .../src/assembly/project/constants.rs | 1 - .../main/assemble_project_resources.rs | 3 +- .../assembly/project/main/sql_syntax_error.rs | 4 - .../src/assembly/project/models.rs | 3 +- .../compiler/_helpers/sql_tests/assembly.rs | 3 +- .../_helpers/sql_tests/case_identity.rs | 10 +- .../compiler/_helpers/sql_tests/mock_reads.rs | 6 +- .../sqlbuild-analysis/src/compiler/models.rs | 3 +- .../src/contracts/_helpers/columns.rs | 2 +- .../src/contracts/_helpers/dynamic.rs | 2 +- .../src/contracts/_helpers/type_comparison.rs | 3 +- .../main/evaluate_model_contracts.rs | 6 +- .../tests/test_evaluate_model_contracts.rs | 43 ++-- .../src/contracts/tests/test_types.rs | 6 + .../src/lineage/_helpers/parsed_lineage.rs | 6 +- .../src/lineage/_helpers/projections.rs | 3 +- .../src/lineage/main/build_fast_lineage.rs | 7 +- .../src/lineage/main/parser_dialect.rs | 6 +- .../src/lineage/tests/helpers.rs | 8 +- .../_helpers/explanation/messages.rs | 43 ++-- .../_helpers/sql_text/python_regex.rs | 6 +- .../main/check_semantic_metadata.rs | 4 - .../main/complete_semantic_diagnostics.rs | 4 - .../main/plan_type_recovery.rs | 4 - .../src/semantic_checks/models.rs | 5 +- .../src/semantic_checks/tests/helpers.rs | 24 +-- .../src/semantic_validation/main.rs | 1 + .../main/new_analysis_pool.rs | 6 + .../src/type_system/_helpers/parsed.rs | 8 +- .../src/type_system/_helpers/python_text.rs | 6 +- .../src/type_system/_helpers/shallow_args.rs | 3 - .../src/type_system/constants.rs | 2 + .../src/type_system/errors.rs | 21 ++ .../src/type_system/main/normalize_type.rs | 7 +- .../sqlbuild-analysis/src/type_system/mod.rs | 1 + .../src/type_system/models.rs | 24 +-- .../type_system/tests/test_normalize_type.rs | 2 +- .../src/panics/main/native_failure.rs | 3 +- .../src/text/_helpers/active_text.rs | 28 +++ .../src/text/_helpers/case_tables.rs | 3 +- crates/sqlbuild-core/src/text/_helpers/mod.rs | 1 + .../src/text/main/active_python_text.rs | 25 +-- crates/sqlbuild-core/src/text/main/mod.rs | 1 + .../src/text/main/python_decimal_value.rs | 2 - .../src/text/main/python_ignorecase_key.rs | 12 +- .../src/text/main/set_active_python_text.rs | 9 + .../src/text/tests/test_python_compat.rs | 120 +++++++++-- .../src/text/tests/test_types.rs | 8 + .../analysis_session/nullability_rules.rs | 2 +- .../analysis_session/query_columns.rs | 15 +- .../_helpers/analysis_session/session.rs | 8 +- .../_helpers/boundary/python_runtime.rs | 2 +- .../_helpers/contracts/model_contracts.rs | 2 +- .../_helpers/project_assembly/resources.rs | 10 +- .../_helpers/render/macro_call_rows.rs | 3 +- .../bindings/_helpers/render/macro_calls.rs | 7 +- .../_helpers/semantic_checks/completion.rs | 11 +- .../_helpers/semantic_checks/session_facts.rs | 8 +- .../_helpers/semantic_checks/type_recovery.rs | 10 +- .../_helpers/sql_test_glue/assembly.rs | 3 +- .../_helpers/type_system/normalization.rs | 8 +- .../src/bindings/main/register.rs | 2 +- .../src/macro_calls/_helpers/scanner.rs | 8 +- .../sqlbuild-render/src/macro_calls/errors.rs | 23 +++ .../src/macro_calls/main/macro_call_names.rs | 6 +- .../macro_calls/main/scan_macro_call_sites.rs | 28 ++- crates/sqlbuild-render/src/macro_calls/mod.rs | 1 + .../sqlbuild-render/src/macro_calls/models.rs | 27 +-- .../src/macro_calls/tests/helpers.rs | 7 + .../tests/test_macro_call_names.rs | 93 +++++---- .../tests/test_scan_macro_call_sites.rs | 2 +- .../src/macro_calls/tests/test_types.rs | 15 +- .../sqlbuild-sqltext/src/compiler/main/mod.rs | 1 + .../src/compiler/main/placeholder_defaults.rs | 43 ++++ crates/sqlbuild-sqltext/src/constants.rs | 2 + fensu.toml | 10 + .../_helpers/corpus/emitted_codes.py | 8 +- scripts/compiler_differential/constants.py | 6 + scripts/python_char_table/_helpers/table.py | 7 +- ...ullability.py => safe_cast_nullability.py} | 0 .../analysis_session/_helpers/profile_rows.py | 11 +- .../_helpers/query_columns.py | 47 +++-- .../_helpers/assembly/metadata_validation.py | 7 +- .../_helpers/macro_bridge/expansion.py | 14 +- .../_helpers/native_stages/assembly.py | 2 +- .../_helpers/native_stages/sql_tests.py | 5 +- .../compile/_helpers/render/declarations.py | 2 +- .../compile/main/expand_sql_with_spans.py | 2 +- .../main/_require_supported_python.py | 14 ++ ...fallback.py => _report_native_fallback.py} | 0 src/sqlbuild/compiler/frontier/types.py | 3 +- .../lineage/_helpers/native_fast_columns.py | 3 +- src/sqlbuild/compiler/lineage/exceptions.py | 4 + .../macro_bridge/classes/macro_bridge.py | 5 +- .../_scan_macro_calls.py} | 26 +-- .../macro_bridge/main/_splice_macro_calls.py | 28 +++ .../main/run_with_macro_bridge.py | 6 +- src/sqlbuild/compiler/sql_test_glue/models.py | 3 +- src/sqlbuild/sql_values/main/identity.py | 10 - .../sqlbuild/adapter/type_system/helpers.py | 9 + .../test_native_type_normalization.py | 9 +- .../src/sqlbuild/cli/commands/main/helpers.py | 1 - .../compiler/analysis_session/_test_types.py | 2 +- .../compiler/analysis_session/helpers.py | 111 +++++++++- .../test_native_cte_fact_recovery.py | 2 +- .../test_native_model_analysis.py | 76 +++---- .../test_native_query_columns.py | 8 +- .../test_shared_query_outputs.py | 56 ++---- .../compile/test_native_macro_bridge.py | 2 +- .../contracts/test_native_model_contracts.py | 5 +- .../src/sqlbuild/compiler/golden_views.py | 123 ------------ .../src/sqlbuild/compiler/helpers.py | 189 ++++++++++++++++++ .../lineage/test_native_fast_lineage.py | 41 +++- .../src/sqlbuild/compiler/pipeline/helpers.py | 17 ++ .../pipeline/test_expression_catalog_cache.py | 17 +- .../test_native_project_assembly.py | 12 +- .../compiler/semantic_checks/helpers.py | 44 +++- .../test_native_metadata_checks.py | 15 +- .../test_native_semantic_checks.py | 6 +- .../compiler/sql_test_glue/helpers.py | 10 +- .../test_native_sql_test_assembly.py | 2 +- .../compiler/compile/_helpers/helpers.py | 38 +++- .../_helpers/test_sql_analysis_columns.py | 36 +--- 142 files changed, 1463 insertions(+), 1115 deletions(-) delete mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/publication.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs delete mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs create mode 100644 crates/sqlbuild-analysis/src/semantic_validation/main/new_analysis_pool.rs create mode 100644 crates/sqlbuild-analysis/src/type_system/errors.rs create mode 100644 crates/sqlbuild-core/src/text/_helpers/active_text.rs create mode 100644 crates/sqlbuild-core/src/text/main/set_active_python_text.rs create mode 100644 crates/sqlbuild-render/src/macro_calls/errors.rs create mode 100644 crates/sqlbuild-sqltext/src/compiler/main/placeholder_defaults.rs rename src/sqlbuild/adapter/type_system/main/{_safe_cast_nullability.py => safe_cast_nullability.py} (100%) create mode 100644 src/sqlbuild/compiler/discovery/main/_require_supported_python.py rename src/sqlbuild/compiler/frontier/main/{report_native_fallback.py => _report_native_fallback.py} (100%) rename src/sqlbuild/compiler/macro_bridge/{_helpers/call_sites.py => main/_scan_macro_calls.py} (60%) create mode 100644 src/sqlbuild/compiler/macro_bridge/main/_splice_macro_calls.py delete mode 100644 src/sqlbuild/sql_values/main/identity.py delete mode 100644 tests/integration/src/sqlbuild/compiler/golden_views.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs index 2ab86d737f..47310708ff 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs @@ -1,13 +1,15 @@ -//! A session copy of the compile's binding catalog that records what Python's would gain. +//! The session's binding catalog copy and Python's published relation shapes. use std::collections::{HashMap, HashSet}; use crate::assembly::analysis_session::_helpers::compact_batch::{MemoKey, SharedResult}; use crate::assembly::analysis_session::_helpers::mappings::{ - ShapeTable, catalog_columns, catalog_relations, same_mapping, + ShapeTable, catalog_columns, catalog_relations, same_mapping, with_pair, }; +use crate::assembly::analysis_session::constants::{QUOTED_IDENTIFIER_DELIMITER, UNKNOWN_TYPE}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; -use crate::semantic_validation::models::{Columns, ProjectCatalog}; +use crate::semantic_validation::main::normalize::normalize_analysis_sql; +use crate::semantic_validation::models::{Columns, NormalizationInput, ProjectCatalog}; use crate::semantic_validation::types::{BindingRequest, Relations}; /// Python's `BindingCatalog` state that model analysis reads and changes. @@ -143,3 +145,71 @@ impl SessionCatalog { (self.additions, self.analysis_names) } } + +/// How a session publishes relation shapes. +pub(crate) struct ShapeOptions<'a> { + pub(crate) dialect: &'a str, + pub(crate) case_sensitive: bool, +} + +/// One relation's inferred columns and the SQL and inputs they came from. +pub(crate) struct ShapeSource<'a> { + pub(crate) sql: &'a str, + pub(crate) columns: Pairs, + pub(crate) inputs: &'a Shapes, + pub(crate) snapshot_columns: Option<&'a (String, String)>, +} + +impl SessionCatalog { + /// A model relation's shape, including snapshot validity columns. + pub(crate) fn published_model_shape( + &self, + options: &ShapeOptions<'_>, + source: ShapeSource<'_>, + ) -> Result { + let snapshot_columns: Option<&(String, String)> = source.snapshot_columns; + let mut shape: Pairs = self.inferred_binding_shape(options, source)?; + if let Some((valid_from, valid_to)) = snapshot_columns { + shape = with_pair(shape, valid_from, UNKNOWN_TYPE); + shape = with_pair(shape, valid_to, UNKNOWN_TYPE); + } + Ok(shape) + } + + /// Column names keep authored quoting only where the dialect binds quoted names exactly. + pub(crate) fn inferred_binding_shape( + &self, + options: &ShapeOptions<'_>, + source: ShapeSource<'_>, + ) -> Result { + if !options.case_sensitive { + return Ok(source.columns); + } + let normalized: String = normalize_analysis_sql(NormalizationInput { + sql: source.sql.to_owned(), + dialect: options.dialect.to_owned(), + stubs: HashMap::new(), + placeholders: HashMap::new(), + })?; + if !normalized.contains(QUOTED_IDENTIFIER_DELIMITER) && !has_exact_input(source.inputs) { + return Ok(source.columns); + } + Ok(self + .native + .inferred_schema( + &normalized, + catalog_columns(&source.columns), + catalog_relations(source.inputs), + )? + .into_iter() + .map(|(name, data_type)| (name, data_type.unwrap_or_default())) + .collect()) + } +} + +fn has_exact_input(inputs: &Shapes) -> bool { + inputs + .iter() + .flat_map(|(_, shape)| shape.iter()) + .any(|(name, _)| name.contains(QUOTED_IDENTIFIER_DELIMITER)) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs index fa40ab5478..536c87100d 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs @@ -32,8 +32,9 @@ use crate::assembly::analysis_session::constants::{ CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, }; +use crate::assembly::analysis_session::models::NullabilityCallback; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; -use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; +use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::type_system::main::normalize_type::normalize_type; use crate::type_system::models::{NormalizedType, TypeFamily}; @@ -108,8 +109,7 @@ pub(crate) struct LegacyInput<'a> { /// Python's `lineage_reference_map` items. pub(crate) lineage_references: &'a [(String, String, String)], pub(crate) recover: bool, - /// Python's `_infer_columns_from_polyglot_ast`, which infers nullability through input - /// columns even where no input nullability is known; lineage analysis stays shallow there. + /// Python's `_infer_columns_from_polyglot_ast`: nullability inferred through input columns. pub(crate) full_nullability: bool, /// Python's `column_types_by_table`: every known relation's types. pub(crate) types: &'a ShapeTable, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs index 29939adaf5..f1a4d18ad2 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs @@ -177,7 +177,10 @@ pub(crate) enum PyValue { /// The wheel's guarded `parse_one`; Ok(None) is the `PolyglotError` Python catches. pub(crate) fn parse_one(sql: &str, dialect: &str) -> Result, String> { - Ok(parse_one_or_error(sql, dialect)?.ok()) + match parse_one_or_error(sql, dialect)? { + Ok(parsed) => Ok(Some(parsed)), + Err(_polyglot_error) => Ok(None), + } } /// The wheel's guarded `parse_one`, with the message of the `PolyglotError` it raises. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index eceaf3610d..d1589b8930 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -85,11 +85,7 @@ enum Boundary<'a> { Passthrough(&'a str), } -/// The proof Python computes. -/// -/// # Errors -/// -/// An internal native failure: a panic, or a parsed tree the proof cannot read. +/// The proof Python computes; fails on a panic or a parsed tree the proof cannot read. pub(crate) fn pivot_outcome( sql: &str, families: &[DynamicFamily], diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index 3f5eefe01d..45f4427c36 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -19,8 +19,9 @@ use crate::assembly::analysis_session::constants::{ TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, WILDCARD, }; +use crate::assembly::analysis_session::models::NullabilityCallback; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow, ModelRequest}; -use crate::assembly::analysis_session::types::{NullabilityCallback, Pairs, Shapes}; +use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::main::normalize::normalize_analysis_sql; use crate::semantic_validation::models::NormalizationInput; @@ -53,11 +54,7 @@ enum Projected { /// One projection's upstream sources and confidence code. type Upstream = (Vec<(String, String, String)>, u8); -/// Python's compact re-analysis. -/// -/// # Errors -/// -/// An internal native failure, panics included. +/// Python's compact re-analysis. # Errors An internal native failure, panics included. pub(crate) fn enrichment(input: &EnrichmentInput<'_>) -> Result { catch_compiler_panic(|| analysed(input)) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs index 30aa4a8a3e..61615bd519 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs @@ -6,5 +6,5 @@ pub(crate) mod dict_walk; pub(crate) mod dynamic_pivot; pub(crate) mod enrichment; pub(crate) mod mappings; -pub(crate) mod publication; +pub(crate) mod query_columns; pub(crate) mod session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/publication.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/publication.rs deleted file mode 100644 index 5032309904..0000000000 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/publication.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! Python's `published_model_shape` and `inferred_binding_shape`. - -use std::collections::HashMap; - -use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; -use crate::assembly::analysis_session::_helpers::mappings::{ - catalog_columns, catalog_relations, with_pair, -}; -use crate::assembly::analysis_session::constants::{QUOTED_IDENTIFIER_DELIMITER, UNKNOWN_TYPE}; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; -use crate::semantic_validation::main::normalize::normalize_analysis_sql; -use crate::semantic_validation::models::NormalizationInput; - -/// How a session publishes relation shapes. -pub(crate) struct ShapeOptions<'a> { - pub(crate) dialect: &'a str, - pub(crate) case_sensitive: bool, -} - -/// One relation's inferred columns and the SQL and inputs they came from. -pub(crate) struct ShapeSource<'a> { - pub(crate) sql: &'a str, - pub(crate) columns: Pairs, - pub(crate) inputs: &'a Shapes, - pub(crate) snapshot_columns: Option<&'a (String, String)>, -} - -impl SessionCatalog { - /// A model relation's shape, including snapshot validity columns. - pub(crate) fn published_model_shape( - &self, - options: &ShapeOptions<'_>, - source: ShapeSource<'_>, - ) -> Result { - let snapshot_columns: Option<&(String, String)> = source.snapshot_columns; - let mut shape: Pairs = self.inferred_binding_shape(options, source)?; - if let Some((valid_from, valid_to)) = snapshot_columns { - shape = with_pair(shape, valid_from, UNKNOWN_TYPE); - shape = with_pair(shape, valid_to, UNKNOWN_TYPE); - } - Ok(shape) - } - - /// Column names keep authored quoting only where the dialect binds quoted names exactly. - pub(crate) fn inferred_binding_shape( - &self, - options: &ShapeOptions<'_>, - source: ShapeSource<'_>, - ) -> Result { - if !options.case_sensitive { - return Ok(source.columns); - } - let normalized: String = normalize_analysis_sql(NormalizationInput { - sql: source.sql.to_owned(), - dialect: options.dialect.to_owned(), - stubs: HashMap::new(), - placeholders: HashMap::new(), - })?; - if !normalized.contains(QUOTED_IDENTIFIER_DELIMITER) && !has_exact_input(source.inputs) { - return Ok(source.columns); - } - Ok(self - .native - .inferred_schema( - &normalized, - catalog_columns(&source.columns), - catalog_relations(source.inputs), - )? - .into_iter() - .map(|(name, data_type)| (name, data_type.unwrap_or_default())) - .collect()) - } -} - -fn has_exact_input(inputs: &Shapes) -> bool { - inputs - .iter() - .flat_map(|(_, shape)| shape.iter()) - .any(|(name, _)| name.contains(QUOTED_IDENTIFIER_DELIMITER)) -} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs new file mode 100644 index 0000000000..3acf6272a7 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs @@ -0,0 +1,172 @@ +//! Python's per-query column analysis outside the session, behind its entry point. + +use std::collections::HashMap; + +use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; + +use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; +use crate::assembly::analysis_session::_helpers::compact_batch::{ + Batch, BatchMember, MemberAnalysis, MemberResult, +}; +use crate::assembly::analysis_session::_helpers::cte_facts::{ + LegacyAnalysis, LegacyInput, RecoveryProfile, legacy_analysis, +}; +use crate::assembly::analysis_session::_helpers::enrichment::{ + Enrichment, EnrichmentInput, enrichment, +}; +use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; +use crate::assembly::analysis_session::models::{ + ModelReference, ModelRequest, QueryColumns, QueryColumnsInput, QueryColumnsMode, + QueryColumnsRequest, +}; +use crate::semantic_validation::models::ProjectCatalog; + +/// Each query's columns, in order; only `Batch` queries read the catalog. +pub(crate) fn query_columns( + catalog: Option<&ProjectCatalog>, + request: &QueryColumnsRequest, +) -> Result, String> { + let types: ShapeTable = ShapeTable::from_shapes(&request.types); + let nullability: ShapeTable = ShapeTable::from_shapes(&request.nullability); + let analysed: Vec<(usize, &QueryColumnsInput)> = request + .queries + .iter() + .enumerate() + .filter(|(_, query)| query.mode == QueryColumnsMode::Batch) + .collect(); + let mut batch_results: HashMap = HashMap::new(); + if !analysed.is_empty() { + let catalog: &ProjectCatalog = + catalog.ok_or("a compact query batch without an analysis catalog")?; + let mut session_catalog: SessionCatalog = SessionCatalog::new(catalog, &Vec::new()); + let batch = Batch { + dialect: &request.dialect, + function_return_types: &request.function_return_types, + rich_type_inference: true, + members: analysed + .iter() + .map(|(_, query)| BatchMember { + query_sql: &query.sql, + placeholders: &query.placeholders, + analysis_names: query.analysis_names.iter().map(String::as_str).collect(), + lineage_references: &query.lineage_references, + recover_cte_facts: query.recover_cte_facts, + binding_schema: None, + }) + .collect(), + types: &types, + nullability: &nullability, + }; + let results: Vec = session_catalog.analyze_batch(&batch)?; + batch_results.extend(analysed.iter().map(|(index, _)| *index).zip(results)); + } + let tables = Tables { + request, + types: &types, + nullability: &nullability, + }; + request + .queries + .iter() + .enumerate() + .map(|(index, query)| match batch_results.remove(&index) { + None if query.mode == QueryColumnsMode::Reanalysis => tables.reanalysed(query), + None => tables.parsed( + query, + &parse_cleaned_sql(query), + query.mode == QueryColumnsMode::Parse, + ), + Some(result) => match result.analysis { + MemberAnalysis::Projected { + columns, has_star, .. + } => Ok(QueryColumns { + succeeded: true, + columns: Some(columns), + has_star, + }), + MemberAnalysis::Legacy { .. } => tables.parsed(query, &result.cleaned_sql, false), + MemberAnalysis::Failed => Ok(QueryColumns::default()), + }, + }) + .collect() +} + +/// Python's `substitute_placeholder_defaults` over SQL its caller already normalized. +fn parse_cleaned_sql(query: &QueryColumnsInput) -> String { + if query.placeholders.is_empty() { + query.sql.clone() + } else { + placeholder_defaults(&query.sql, &query.placeholders) + } +} + +struct Tables<'a> { + request: &'a QueryColumnsRequest, + types: &'a ShapeTable, + nullability: &'a ShapeTable, +} + +impl Tables<'_> { + /// Python's `analyze_columns_and_lineage_with_polyglot` without a precomputed analysis. + fn reanalysed(&self, query: &QueryColumnsInput) -> Result { + let model = ModelRequest { + query_sql: query.sql.clone(), + placeholders: query.placeholders.clone(), + references: query + .analysis_names + .iter() + .map(|analysis_name| ModelReference { + analysis_name: analysis_name.clone(), + model_ref: false, + }) + .collect(), + lineage_references: query.lineage_references.clone(), + recover_cte_facts: query.recover_cte_facts, + ..ModelRequest::default() + }; + let enriched: Enrichment = enrichment(&EnrichmentInput { + model: &model, + input_schemas: &self.request.types, + dialect: &self.request.dialect, + function_return_types: &self.request.function_return_types, + nullability_rules: self.request.nullability_rules.as_ref(), + nullability_callback: self.request.nullability_callback.as_ref(), + })?; + Ok(QueryColumns { + succeeded: enriched.analysis_succeeded, + columns: enriched.columns, + has_star: enriched.has_star, + }) + } + + /// Python's analysis of the parsed tree. + fn parsed( + &self, + query: &QueryColumnsInput, + cleaned_sql: &str, + full_nullability: bool, + ) -> Result { + let analysis: LegacyAnalysis = catch_compiler_panic(|| { + legacy_analysis(&LegacyInput { + cleaned_sql, + lineage_references: &query.lineage_references, + recover: query.recover_cte_facts, + full_nullability, + types: self.types, + nullability: self.nullability, + profile: RecoveryProfile { + dialect: &self.request.dialect, + function_return_types: &self.request.function_return_types, + rules: self.request.nullability_rules.as_ref(), + callback: self.request.nullability_callback.as_ref(), + }, + }) + })?; + Ok(QueryColumns { + succeeded: analysis.succeeded, + columns: analysis.columns, + has_star: analysis.has_star, + }) + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 0ff17df9c3..54eadd5488 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -14,6 +14,7 @@ use crate::assembly::analysis_session::_helpers::analysis_cache::{ CachedOutcome, decode_outcome, encode_outcome, }; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; +use crate::assembly::analysis_session::_helpers::catalog_state::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::_helpers::compact_batch::{ Batch, BatchMember, MemberAnalysis, MemberResult, }; @@ -28,7 +29,6 @@ use crate::assembly::analysis_session::_helpers::mappings::{ ShapeTable, dict_from_pairs, same_keys, same_relations, }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; -use crate::assembly::analysis_session::_helpers::publication::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, ContractProof, FinishedSession, LineageFacts, LineageRow, @@ -44,11 +44,6 @@ const SESSION_CONTEXT: &str = "native model analysis"; impl AnalysisSession { /// A session over `request`; a `ref` cycle is one wave. - /// - /// # Errors - /// - /// A native failure where two models share a name: discovery rejects duplicate model names - /// (D007) before any analysis, so this is a broken invariant. pub(crate) fn start(request: SessionRequest, catalog: &ProjectCatalog) -> Result { let producers: Vec> = producers(&request.models) .ok_or_else(|| native_failure(SESSION_CONTEXT, "two analysed models share a name"))?; @@ -341,8 +336,7 @@ impl AnalysisSession { Ok(()) } - /// Python's re-analysis of each candidate natively; the first failure in model order fails - /// the analysis. + /// Python's re-analysis of each candidate; the first failure in model order fails it. fn native_enrichments( &self, candidates: &[(usize, Shapes, bool)], @@ -735,8 +729,7 @@ fn enriched_analysis( merged } -/// A compiled model's typed output columns and lineage from a successful analysis with native -/// lineage. +/// A model's typed output columns and lineage from a successful native-lineage analysis. fn session_model_facts(analysis: &ModelAnalysis) -> Option { let (LineageFacts::Native(rows) | LineageFacts::NativeFacts(rows)) = &analysis.lineage; analysis.analysis_succeeded.then(|| SessionModelFacts { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs index 7f3c903156..b65c4a0ad7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/expression_shapes.rs @@ -3,6 +3,7 @@ use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use crate::assembly::analysis_session::_helpers::catalog_state::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::_helpers::compact_batch::{ Batch, BatchMember, MemberAnalysis, MemberResult, }; @@ -10,7 +11,6 @@ use crate::assembly::analysis_session::_helpers::cte_facts::{ LegacyAnalysis, LegacyInput, RecoveryProfile, legacy_analysis, }; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::_helpers::publication::{ShapeOptions, ShapeSource}; use crate::assembly::analysis_session::_helpers::session::shape_columns; use crate::assembly::analysis_session::models::{ExpressionShape, ExpressionShapeRequest}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs deleted file mode 100644 index 639fb24a1c..0000000000 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_dynamic_proof.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! The dynamic pivot proof a finished analysis session kept for one model. - -use crate::assembly::analysis_session::models::{ContractProof, FinishedSession}; - -/// The named model's proof from the session's relation facts, or None where it declares none. -pub fn finished_dynamic_proof<'a>( - session: &'a FinishedSession, - name: &str, -) -> Option<&'a ContractProof> { - session.dynamic_proofs.get(name) -} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index bc38ac75e7..3abe4d2a9f 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -1,7 +1,6 @@ pub mod attach_analysis_cache; pub mod expression_shapes; pub mod finish_analysis_session; -pub mod finished_dynamic_proof; pub mod finished_fact_models; pub mod finished_model_facts; pub mod prove_dynamic_contracts; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs index a0b4001a96..f880ad0e58 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs @@ -1,180 +1,13 @@ -//! Python's per-query column analysis outside the session: the precomputed compact batch, -//! the compact re-analysis over known input types, or the parsed tree alone. +//! Python's per-query column analysis outside the session. -use std::collections::HashMap; - -use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; - -use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; -use crate::assembly::analysis_session::_helpers::compact_batch::{ - Batch, BatchMember, MemberAnalysis, MemberResult, -}; -use crate::assembly::analysis_session::_helpers::cte_facts::{ - LegacyAnalysis, LegacyInput, RecoveryProfile, legacy_analysis, -}; -use crate::assembly::analysis_session::_helpers::enrichment::{ - Enrichment, EnrichmentInput, enrichment, -}; -use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::models::{ - ModelReference, ModelRequest, QueryColumns, QueryColumnsInput, QueryColumnsMode, - QueryColumnsRequest, -}; -use crate::assembly::project::_helpers::syntax::placeholder_defaults; +use crate::assembly::analysis_session::_helpers::query_columns as columns; +use crate::assembly::analysis_session::models::{QueryColumns, QueryColumnsRequest}; use crate::semantic_validation::models::ProjectCatalog; /// Each query's columns, in order; only `Batch` queries read the catalog. -/// -/// # Errors -/// -/// An internal native failure, such as a panic. pub fn query_columns( catalog: Option<&ProjectCatalog>, request: &QueryColumnsRequest, ) -> Result, String> { - let types: ShapeTable = ShapeTable::from_shapes(&request.types); - let nullability: ShapeTable = ShapeTable::from_shapes(&request.nullability); - let analysed: Vec<(usize, &QueryColumnsInput)> = request - .queries - .iter() - .enumerate() - .filter(|(_, query)| query.mode == QueryColumnsMode::Batch) - .collect(); - let mut batch_results: HashMap = HashMap::new(); - if !analysed.is_empty() { - let catalog: &ProjectCatalog = - catalog.ok_or("a compact query batch without an analysis catalog")?; - let mut session_catalog: SessionCatalog = SessionCatalog::new(catalog, &Vec::new()); - let batch = Batch { - dialect: &request.dialect, - function_return_types: &request.function_return_types, - rich_type_inference: true, - members: analysed - .iter() - .map(|(_, query)| BatchMember { - query_sql: &query.sql, - placeholders: &query.placeholders, - analysis_names: query.analysis_names.iter().map(String::as_str).collect(), - lineage_references: &query.lineage_references, - recover_cte_facts: query.recover_cte_facts, - binding_schema: None, - }) - .collect(), - types: &types, - nullability: &nullability, - }; - let results: Vec = session_catalog.analyze_batch(&batch)?; - batch_results.extend(analysed.iter().map(|(index, _)| *index).zip(results)); - } - let tables = Tables { - request, - types: &types, - nullability: &nullability, - }; - request - .queries - .iter() - .enumerate() - .map(|(index, query)| match batch_results.remove(&index) { - None if query.mode == QueryColumnsMode::Reanalysis => tables.reanalysed(query), - None => tables.parsed( - query, - &parse_cleaned_sql(query), - query.mode == QueryColumnsMode::Parse, - ), - Some(result) => match result.analysis { - MemberAnalysis::Projected { - columns, has_star, .. - } => Ok(QueryColumns { - succeeded: true, - columns: Some(columns), - has_star, - }), - MemberAnalysis::Legacy { .. } => tables.parsed(query, &result.cleaned_sql, false), - MemberAnalysis::Failed => Ok(QueryColumns::default()), - }, - }) - .collect() -} - -/// Python's `substitute_placeholder_defaults` over SQL its caller already normalized. -fn parse_cleaned_sql(query: &QueryColumnsInput) -> String { - if query.placeholders.is_empty() { - query.sql.clone() - } else { - placeholder_defaults(&query.sql, &query.placeholders) - } -} - -struct Tables<'a> { - request: &'a QueryColumnsRequest, - types: &'a ShapeTable, - nullability: &'a ShapeTable, -} - -impl Tables<'_> { - /// Python's `analyze_columns_and_lineage_with_polyglot` without a precomputed analysis: - /// compact analysis over the input types, every input column's nullability unknown, then - /// the parsed tree where compact analysis declines. - fn reanalysed(&self, query: &QueryColumnsInput) -> Result { - let model = ModelRequest { - query_sql: query.sql.clone(), - placeholders: query.placeholders.clone(), - references: query - .analysis_names - .iter() - .map(|analysis_name| ModelReference { - analysis_name: analysis_name.clone(), - model_ref: false, - }) - .collect(), - lineage_references: query.lineage_references.clone(), - recover_cte_facts: query.recover_cte_facts, - ..ModelRequest::default() - }; - let enriched: Enrichment = enrichment(&EnrichmentInput { - model: &model, - input_schemas: &self.request.types, - dialect: &self.request.dialect, - function_return_types: &self.request.function_return_types, - nullability_rules: self.request.nullability_rules.as_ref(), - nullability_callback: self.request.nullability_callback.as_ref(), - })?; - Ok(QueryColumns { - succeeded: enriched.analysis_succeeded, - columns: enriched.columns, - has_star: enriched.has_star, - }) - } - - /// Python's analysis of the parsed tree: `_infer_columns_from_polyglot_ast` where - /// `full_nullability`, else `_analyze_columns_and_lineage_from_polyglot_ast`. - fn parsed( - &self, - query: &QueryColumnsInput, - cleaned_sql: &str, - full_nullability: bool, - ) -> Result { - let analysis: LegacyAnalysis = catch_compiler_panic(|| { - legacy_analysis(&LegacyInput { - cleaned_sql, - lineage_references: &query.lineage_references, - recover: query.recover_cte_facts, - full_nullability, - types: self.types, - nullability: self.nullability, - profile: RecoveryProfile { - dialect: &self.request.dialect, - function_return_types: &self.request.function_return_types, - rules: self.request.nullability_rules.as_ref(), - callback: self.request.nullability_callback.as_ref(), - }, - }) - })?; - Ok(QueryColumns { - succeeded: analysis.succeeded, - columns: analysis.columns, - has_star: analysis.has_star, - }) - } + columns::query_columns(catalog, request) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs index 91e547cc5a..1eabc81c30 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/start_analysis_session.rs @@ -3,11 +3,7 @@ use crate::assembly::analysis_session::models::{AnalysisSession, SessionRequest}; use crate::semantic_validation::models::ProjectCatalog; -/// A session over `request` on a copy of `catalog`. -/// -/// # Errors -/// -/// An internal native failure where the request breaks the session's invariants. +/// A session over `request` on a copy of `catalog`; fails on broken session invariants. pub fn start_analysis_session( request: SessionRequest, catalog: &ProjectCatalog, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 873c9d2371..3c48dab595 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -1,6 +1,7 @@ //! Plain-data request, deferrals and outcomes of the native model analysis session. use std::collections::HashMap; +use std::fmt; use std::sync::Arc; use rayon::ThreadPool; @@ -9,7 +10,7 @@ use sqlbuild_cache::store::models::NativeStore; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::types::{NullabilityCallback, OutputSources, Pairs, Shapes}; +use crate::assembly::analysis_session::types::{NullabilityRuleFn, OutputSources, Pairs, Shapes}; use crate::semantic_validation::types::DiagnosticRow; /// One `ref`, `source`, `seed`, `table_fn` or `udf` call a model's SQL makes. @@ -97,11 +98,10 @@ pub struct FinishedSession { pub(crate) pool: Result, String>, pub(crate) models: HashMap, /// Each model's dynamic pivot proof from the session's own relation facts, by model name. - pub(crate) dynamic_proofs: HashMap, + pub dynamic_proofs: HashMap, } -/// A compiled model's typed output columns and lineage from an analysis the session completed -/// natively. +/// A compiled model's typed output columns and lineage from a native session analysis. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SessionModelFacts { pub columns: Option>, @@ -251,7 +251,6 @@ pub struct AnalysisCache { pub(crate) session_digest: ContentDigest, pub(crate) keys: Vec>, pub(crate) hits: Vec, - /// Models whose outcome must not be stored: a logged failure or Python's answer. /// The whole-table digest each legacy analysis read; its entry also requires it. pub(crate) legacy_tables: Vec>, pub(crate) stats: AnalysisCacheStats, @@ -286,8 +285,7 @@ pub enum QueryColumnsMode { Reanalysis, /// `infer_columns_with_sql_analysis`: the parsed tree only. Parse, - /// `analyze_columns_and_lineage_with_polyglot` with compact analysis off: the parsed tree, - /// nullability shallow where no input nullability is known. + /// `analyze_columns_and_lineage_with_polyglot` with compact analysis off: the parsed tree. Legacy, } @@ -353,3 +351,13 @@ pub struct CteFacts { /// Sorted outputs a filter proves non-null. pub non_null_outputs: Vec, } + +/// The callback that runs project-local adapter nullability rules, the `python` rule id. +#[derive(Clone)] +pub struct NullabilityCallback(pub Arc); + +impl fmt::Debug for NullabilityCallback { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("NullabilityCallback") + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 07c399b61a..511ad6562d 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -10,7 +10,6 @@ use sqlbuild_cache::store::models::NativeStore; use crate::assembly::analysis_session::main::attach_analysis_cache::attach_analysis_cache; use crate::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; -use crate::assembly::analysis_session::main::finished_dynamic_proof::finished_dynamic_proof; use crate::assembly::analysis_session::main::finished_fact_models::finished_fact_models; use crate::assembly::analysis_session::main::finished_model_facts::finished_model_facts; use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_session; @@ -289,7 +288,7 @@ pub(crate) fn kept_dynamic_proofs( dialect: &str, sql: &str, family: Option<(&str, &str, &str)>, -) -> Vec> { +) -> Vec { let pivots: PivotBatchRequest = pivot_request(dialect, sql, family); let names: [&str; 2] = ["customer_amounts", "plain"]; let models: Vec = names @@ -314,7 +313,13 @@ pub(crate) fn kept_dynamic_proofs( let (_, _, finished) = completed(started(request)); names .iter() - .map(|name| finished_dynamic_proof(&finished, name).cloned()) + .map(|name| { + finished + .dynamic_proofs + .get(*name) + .cloned() + .map_or(PivotOutcome::Absent, PivotOutcome::Proof) + }) .collect() } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 12c587ab81..a680116d55 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -656,15 +656,11 @@ fn given_a_finished_session_when_reading_dynamic_proofs_then_keeps_each_models_p ), }]; for test_case in test_cases { - let PivotOutcome::Proof(expected) = test_case.expected_outcome else { - panic!("{}: the case expects a proof", test_case.description); - }; - let kept = kept_dynamic_proofs(test_case.dialect, test_case.sql, test_case.family); assert_eq!( kept, - vec![Some(expected), None], + vec![test_case.expected_outcome, PivotOutcome::Absent], "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs index b1f7ddcc37..913e016439 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs @@ -1,23 +1,9 @@ //! Ordered mappings shared with Python's dicts. -use std::fmt; -use std::sync::Arc; - -/// An adapter's own Python nullability rule: `(declared function name, argument nullabilities)` -/// to the rule's nullability, or the failure the rule raised. +/// An adapter's Python nullability rule: `(function name, argument nullabilities)` to a result. pub type NullabilityRuleFn = dyn Fn(&str, &[&'static str]) -> Result<&'static str, String> + Send + Sync; -/// The callback that runs project-local adapter nullability rules, the `python` rule id. -#[derive(Clone)] -pub struct NullabilityCallback(pub Arc); - -impl fmt::Debug for NullabilityCallback { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("NullabilityCallback") - } -} - /// An ordered `name -> value` mapping in Python dict order. pub type Pairs = Vec<(String, String)>; /// Ordered relation shapes in Python dict order. diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs index 5f613005b1..ad475e58be 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs @@ -6,13 +6,12 @@ use polyglot_sql::{ ComplexityGuardOptions, Dialect, DialectType, ParseOptions, ValidationOptions, validate_with_dialect, }; -use sqlbuild_core::text::main::active_python_text::active_python_text; -use sqlbuild_core::text::main::is_python_word::is_python_word; use crate::assembly::project::constants::{ - INVALID_SQL, MAX_FUNCTION_CALL_DEPTH, NORMALIZATION_DEFERRAL, PLACEHOLDER_PREFIX, - UNSUPPORTED_DIALECT_DEFERRAL, + INVALID_SQL, MAX_FUNCTION_CALL_DEPTH, NORMALIZATION_DEFERRAL, UNSUPPORTED_DIALECT_DEFERRAL, }; +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; + use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure, SyntaxMode}; use crate::assembly::project::types::Fact; use crate::lineage::main::parser_dialect::parser_dialect; @@ -29,10 +28,6 @@ pub(crate) fn syntax_valid(check: &SyntaxCheck, dialect: &str) -> Fact { } /// Python's syntax error message for one SQL string, None where Polyglot accepts it. -/// -/// # Errors -/// -/// Where Python raises instead: SQL the analysis normalization rejects, or an unknown dialect. pub(crate) fn syntax_error( check: &SyntaxCheck, dialect: &str, @@ -51,7 +46,7 @@ pub(crate) fn syntax_error( placeholder_defaults(&cleaned, &check.placeholders) }; let dialect_type: DialectType = parser_dialect(Some(dialect)) - .ok_or_else(|| SyntaxFailure::UnknownDialect(dialect.to_owned()))?; + .map_err(|unknown| SyntaxFailure::UnknownDialect(unknown.to_owned()))?; let complexity_guard = ComplexityGuardOptions { max_function_call_depth: Some(MAX_FUNCTION_CALL_DEPTH), ..Default::default() @@ -84,40 +79,3 @@ pub(crate) fn syntax_error( } }) } - -/// `substitute_placeholder_defaults`: `@@@name` becomes its default; unknown names stay. -pub(crate) fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> String { - let mut result: String = String::with_capacity(sql.len()); - let mut rest: &str = sql; - while let Some(start) = rest.find(PLACEHOLDER_PREFIX) { - result.push_str(&rest[..start]); - let after: &str = &rest[start + PLACEHOLDER_PREFIX.len()..]; - let length: usize = after - .find(|character: char| !is_word(character)) - .unwrap_or(after.len()); - if length == 0 { - result.push_str(&rest[start..=start]); - rest = &rest[start + 1..]; - continue; - } - let name: &str = &after[..length]; - match placeholders - .iter() - .find(|(placeholder, _)| placeholder == name) - { - Some((_, default)) => result.push_str(default), - None => { - result.push_str(PLACEHOLDER_PREFIX); - result.push_str(name); - } - } - rest = &after[length..]; - } - result.push_str(rest); - result -} - -/// A character Python's `\w` matches. -fn is_word(character: char) -> bool { - is_python_word(active_python_text(), character) -} diff --git a/crates/sqlbuild-analysis/src/assembly/project/constants.rs b/crates/sqlbuild-analysis/src/assembly/project/constants.rs index 37a401397f..e190f17f47 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/constants.rs @@ -16,7 +16,6 @@ pub(crate) const TEMPLATE_OPEN_TOKEN: &str = "${"; pub(crate) const PYTHON_NONE: &str = "None"; pub(crate) const PYTHON_TRUE: &str = "True"; pub(crate) const PYTHON_FALSE: &str = "False"; -pub(crate) const PLACEHOLDER_PREFIX: &str = "@@@"; pub(crate) const MAX_FUNCTION_CALL_DEPTH: usize = 512; pub(crate) const MODEL_NAME_CONTEXT: &str = "model.name"; pub(crate) const MODEL_DATABASE_CONTEXT: &str = "model.database"; diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs index bce5c8f38f..e873ff0692 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs @@ -65,8 +65,7 @@ fn resources(request: &ProjectRequest) -> Fact { }) } -/// Whether Python's model and hook syntax validation passes; where it raises, Python's -/// validation runs for that model and reports the failure. +/// Whether Python's model and hook syntax validation passes. fn valid_syntax(model: &ModelFacts, dialect: &str) -> bool { model .syntax_checks diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs b/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs index 34e479db15..14449abeeb 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/sql_syntax_error.rs @@ -4,10 +4,6 @@ use crate::assembly::project::_helpers::syntax::syntax_error; use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure, SyntaxMode}; /// The message Python's validation raises with, None where Polyglot accepts the SQL. -/// -/// # Errors -/// -/// Where Python raises a different error: rejected normalization or an unknown dialect. pub fn sql_syntax_error( check: &SyntaxCheck, dialect: &str, diff --git a/crates/sqlbuild-analysis/src/assembly/project/models.rs b/crates/sqlbuild-analysis/src/assembly/project/models.rs index 4f26e9abc9..0d71ab5107 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/models.rs @@ -150,8 +150,7 @@ pub struct Namespace { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ProjectResources { pub model_deps: Vec>, - /// Whether every SQL string a model's assembly validates parses; where one does not, - /// Python's validation reports it with its message and location. + /// Whether every SQL string a model's assembly validates parses; Python reports failures. pub model_syntax_valid: Vec, /// A managed source's `(database, schema)`; None where the entry stays as authored. pub sources: Vec, Option)>>, diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs index 8223bb690f..a768faf024 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs @@ -48,8 +48,7 @@ pub(crate) fn assemble_batch(batch: &SqlTestAssemblyBatch) -> Vec, String>; struct Project<'a> { diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs index 2a20c67fd0..801e993b33 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs @@ -17,6 +17,8 @@ pub(crate) enum FingerprintFailure { /// Python's default decimal context: precision and exponent limits `normalize()` applies. const DECIMAL_PRECISION: usize = 28; +/// The largest decimal digit, which carries when rounded up. +const MAX_DIGIT: u8 = 9; const DECIMAL_EMAX: i64 = 999_999; const DECIMAL_EMIN: i64 = -999_999; /// The smallest exponent of a subnormal result: `Emin - prec + 1`. @@ -141,8 +143,7 @@ fn identities(items: &[SqlTestParameterValue]) -> Result Vec { if dropped > coefficient.len() { return vec![0]; @@ -228,7 +228,7 @@ fn rounded_half_even(coefficient: &[u8], dropped: usize) -> Vec { break; } index -= 1; - if kept[index] == 9 { + if kept[index] == MAX_DIGIT { kept[index] = 0; } else { kept[index] += 1; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs index 8897fd9565..60f90b333b 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs @@ -226,8 +226,7 @@ fn fold(text: &str) -> String { python_casefold(active_python_text(), text) } -/// Python's `scan_sql_reference_calls(...).references`; a failing scan raises its unlocated -/// `CompileInputError`. +/// Python's `scan_sql_reference_calls(...).references`; a failing scan raises unlocated. fn references(sql: &str, syntax: &LexicalSyntax) -> Deferrable> { match extract_sql_references(sql, syntax) { ReferenceExtraction::Extracted(scan) => { @@ -331,8 +330,7 @@ fn proxy_parse_options() -> Deferrable { }) } -/// Python's `_defined_cte_keys` over `to_dict()`: the folded name of every CTE in a `ctes` list, -/// which only `WITH` clauses hold, at any depth. +/// Python's `_defined_cte_keys` over `to_dict()`. fn defined_cte_keys(parsed: &Expression) -> HashSet { let mut keys: HashSet = HashSet::new(); for node in parsed.dfs() { diff --git a/crates/sqlbuild-analysis/src/compiler/models.rs b/crates/sqlbuild-analysis/src/compiler/models.rs index 673c5a7112..457dd969a1 100644 --- a/crates/sqlbuild-analysis/src/compiler/models.rs +++ b/crates/sqlbuild-analysis/src/compiler/models.rs @@ -271,8 +271,7 @@ pub struct SqlTestAssemblyModelPayload { #[derive(Debug, PartialEq)] pub enum SqlTestAssemblyOutcome { Assembled(AssembledSqlTestFacts), - /// Python's decimal context overflows normalizing a case parameter: Python raises - /// `decimal.Overflow` after reporting the facts' diagnostics and building macro-mock queries. + /// Python's decimal context overflows normalizing a case parameter. FingerprintOverflow(AssembledSqlTestFacts), Failed(SqlTestAssemblyFailure), } diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs index a436fdf4ed..eacc966c75 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs @@ -16,7 +16,7 @@ use crate::contracts::models::{ ContractDiagnostic, ContractLocation, ContractModel, ContractSchema, ContractSeverity, DeclaredColumn, InferredOutputColumn, RelatedOutput, }; -use crate::type_system::models::TypeNormalizationError; +use crate::type_system::errors::TypeNormalizationError; /// Python's `_requires_contract_evaluation`. pub(crate) fn requires_contract_evaluation(model: &ContractModel, implicit: bool) -> bool { diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs index 19a2dee914..9c98d44453 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs @@ -14,7 +14,7 @@ use crate::contracts::constants::{ use crate::contracts::models::{ ContractDiagnostic, ContractLocation, ContractModel, ContractSeverity, DynamicContractProof, }; -use crate::type_system::models::TypeNormalizationError; +use crate::type_system::errors::TypeNormalizationError; /// The dynamic column contract diagnostics of one model, before its fixed columns. pub(crate) fn dynamic_column_diagnostics( diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs index 76c2ca77d4..a922a1dc2a 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs @@ -3,8 +3,9 @@ use std::cell::RefCell; use std::collections::HashMap; +use crate::type_system::errors::TypeNormalizationError; use crate::type_system::main::normalize_type::normalize_type; -use crate::type_system::models::{NormalizedType, TypeNormalization, TypeNormalizationError}; +use crate::type_system::models::{NormalizedType, TypeNormalization}; /// Whether two type strings normalize to the same comparison shape. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs index db0a25dfbc..a26e11545a 100644 --- a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs @@ -5,13 +5,9 @@ use crate::contracts::_helpers::columns::{ }; use crate::contracts::_helpers::type_comparison::TypeComparer; use crate::contracts::models::{ContractDiagnostic, ContractRequest}; -use crate::type_system::models::TypeNormalizationError; +use crate::type_system::errors::TypeNormalizationError; /// Each requested model's diagnostics; models that need no evaluation have none. -/// -/// # Errors -/// -/// The type normalization error Python raises while comparing a model's types. pub fn evaluate_model_contracts( request: &ContractRequest, ) -> Result>, TypeNormalizationError> { diff --git a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs index 09496ff85c..d6eb7f0e35 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs @@ -3,7 +3,9 @@ use crate::contracts::models::{ContractDiagnostic, ContractModel, ContractReques use crate::contracts::tests::helpers::{ declared, dynamic_model, inferred, model, outcome_lines, proof, schema, with_inferred, }; -use crate::contracts::tests::test_types::{ContractTestCase, SharedTypesTestCase}; +use crate::contracts::tests::test_types::{ + ContractTestCase, SharedTypesTestCase, UnknownDialectTestCase, +}; #[test] fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() { @@ -281,20 +283,29 @@ fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own #[test] fn given_a_dialect_polyglot_does_not_know_when_comparing_types_then_python_error_is_returned() { - let request = ContractRequest { - dialect: "motherduck".to_owned(), - implicit_column_contracts: true, - models: vec![with_inferred( - model( - Some("enforced"), - Some(schema(vec![declared("amount", Some("INT"), false)], false)), - ), - vec![inferred("amount", Some("INTEGER"), false)], - )], - }; + let test_cases = [UnknownDialectTestCase { + description: "an enforced contract compared under an unknown dialect", + dialect: "motherduck", + expected_error: "Unknown dialect: motherduck", + }]; + for test_case in test_cases { + let request = ContractRequest { + dialect: test_case.dialect.to_owned(), + implicit_column_contracts: true, + models: vec![with_inferred( + model( + Some("enforced"), + Some(schema(vec![declared("amount", Some("INT"), false)], false)), + ), + vec![inferred("amount", Some("INTEGER"), false)], + )], + }; - assert_eq!( - evaluate_model_contracts(&request).map_err(|error| error.message()), - Err("Unknown dialect: motherduck".to_owned()) - ); + assert_eq!( + evaluate_model_contracts(&request).map_err(|error| error.message()), + Err(test_case.expected_error.to_owned()), + "{}", + test_case.description + ); + } } diff --git a/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs b/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs index 43a0260eb6..13d7a49533 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs @@ -21,3 +21,9 @@ pub(crate) struct PromotionTestCase { pub(crate) expected_models: &'static [usize], pub(crate) expected_help: Option<&'static str>, } + +pub(crate) struct UnknownDialectTestCase { + pub(crate) description: &'static str, + pub(crate) dialect: &'static str, + pub(crate) expected_error: &'static str, +} diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs index fc4bb88769..93f982d537 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/parsed_lineage.rs @@ -35,11 +35,7 @@ pub(crate) struct ParsedModel<'a> { pub(crate) options: &'a Result, } -/// The model's lineage outcome. -/// -/// # Errors -/// -/// An internal native failure: a tree the lineage walk cannot read. +/// The model's lineage outcome; fails on a tree the lineage walk cannot read. pub(crate) fn parsed_model_lineage(model: &ParsedModel<'_>) -> Result { let dialect: DialectType = match model.dialect { Ok(dialect) => dialect, diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs index c289d21b22..eca2b47d55 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/projections.rs @@ -265,8 +265,7 @@ fn column_refs(expression: &Expression) -> Result, Unreada Ok(payload_column_refs(&payload)) } -/// Python's recursive `visit`, depth first in payload order: a `column` dict ends its branch; -/// tuples are never entered. +/// Python's recursive `visit`, depth first in payload order. fn payload_column_refs(root: &PythonValue) -> Vec<(String, String)> { let mut refs: Vec<(String, String)> = Vec::new(); let mut pending: Vec<&PythonValue> = vec![root]; diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index d5685f5dd8..94dcca4ac8 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -15,11 +15,10 @@ use crate::lineage::_helpers::references::physical_resources; use crate::lineage::_helpers::stars::{schema_mapping, star_lineage}; use crate::lineage::main::parser_dialect::parser_dialect; use crate::lineage::models::{FastLineageModel, FastLineageOutcome, FastLineageRequest}; -use crate::semantic_validation::_helpers::catalog::new_analysis_pool; +use crate::semantic_validation::main::new_analysis_pool::new_analysis_pool; use crate::semantic_validation::models::ProjectCatalog; -/// One outcome per model, in request order, run on the compile's deep-stack analysis pool, or -/// on a fresh one for a project restored without its catalog. +/// One outcome per model, in request order, run on the compile's deep-stack analysis pool. pub fn build_fast_lineage( request: &FastLineageRequest, catalog: Option<&ProjectCatalog>, @@ -52,7 +51,7 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec = proxy_parse_options(); request .models diff --git a/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs b/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs index 2c88b27913..fd78378ea2 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/parser_dialect.rs @@ -2,8 +2,8 @@ use polyglot_sql::DialectType; -/// Python's `parse_one(dialect=dialect or "generic")` dialect, if Polyglot knows the name. -pub fn parser_dialect(name: Option<&str>) -> Option { +/// Python's `parse_one(dialect=dialect or "generic")` dialect, or the name Polyglot does not know. +pub fn parser_dialect(name: Option<&str>) -> Result { let name = name.filter(|name| !name.is_empty()).unwrap_or("generic"); - name.parse::().ok() + name.parse::().map_err(|_unknown_dialect| name) } diff --git a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs index fc235a3955..a2876a6ac4 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs @@ -21,10 +21,10 @@ pub(crate) fn outcome_lines( schema: schema(), models: vec![model], }; - match build_fast_lineage(&request, Some(&catalog())) { - Ok(outcomes) => outcomes.into_iter().map(described_outcome).collect(), - Err(error) => vec![("error", Vec::new(), false, Some(error))], - } + build_fast_lineage(&request, Some(&catalog())).map_or_else( + |error| vec![("error", Vec::new(), false, Some(error))], + |outcomes| outcomes.into_iter().map(described_outcome).collect(), + ) } /// A `branches`-deep `UNION ALL` chain built from a caller thread with a `stack_bytes` stack. diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs index cf7e9a55e4..74e41888a0 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/explanation/messages.rs @@ -42,8 +42,7 @@ pub(crate) fn semantic_help(code: &str) -> Option<&'static str> { .map(|(_, help)| *help) } -/// Python's `re.sub(r" \(context: [^)]*\)$", "", message)`: Python's `$` also matches before a -/// final newline, which the suffix then keeps. +/// Python's `re.sub(r" \(context: [^)]*\)$", "", message)`. fn without_context_suffix(message: &str) -> Result { let suffix: &Regex = pattern(&CONTEXT_SUFFIX)?; let (body, newline) = match message.strip_suffix('\n') { @@ -81,6 +80,27 @@ fn upper_type_words(text: &str) -> String { result } +/// The end of the first type word at `start` that also ends on a word boundary. +fn type_word_end( + characters: &[char], + start: usize, + boundary: &impl Fn(usize) -> bool, +) -> Option { + for candidate in TYPE_WORDS { + let end: usize = start + candidate.len(); + if let Some(slice) = characters.get(start..end) + && slice + .iter() + .zip(candidate.chars()) + .all(|(character, letter)| ignorecase_equal(*character, letter)) + && boundary(end) + { + return Some(end); + } + } + None +} + /// Python's `re.finditer(_TYPE_WORDS, text, re.IGNORECASE)` spans, in code points. fn type_word_spans(characters: &[char]) -> Vec<(usize, usize)> { let python = active_python_text(); @@ -93,20 +113,11 @@ fn type_word_spans(characters: &[char]) -> Vec<(usize, usize)> { let mut spans: Vec<(usize, usize)> = Vec::new(); let mut start: usize = 0; while start < characters.len() { - let found: Option = boundary(start) - .then(|| { - TYPE_WORDS.iter().find_map(|candidate| { - let end: usize = start + candidate.len(); - let matches: bool = characters.get(start..end).is_some_and(|slice| { - slice - .iter() - .zip(candidate.chars()) - .all(|(character, letter)| ignorecase_equal(*character, letter)) - }); - (matches && boundary(end)).then_some(end) - }) - }) - .flatten(); + let found: Option = if boundary(start) { + type_word_end(characters, start, &boundary) + } else { + None + }; match found { Some(end) => { spans.push((start, end)); diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs index 4dba074976..ae3af9c7ae 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/sql_text/python_regex.rs @@ -1,8 +1,4 @@ -//! Regexes that match as Python's `re` does for this lane's patterns. -//! -//! Python's `\w`, `\d` and `\s` follow `str.isalnum()`, `str.isdecimal()` and `str.isspace()` of -//! the running Python, and `re.IGNORECASE` also matches `ı`, `İ`, `ſ` and the Kelvin sign to ASCII -//! letters; the `regex` crate differs on each beyond ASCII. Patterns spell those classes out. +//! Regexes matching as Python's `re` does, spelling out classes the `regex` crate reads otherwise. use std::fmt::Write; use std::sync::LazyLock; diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs index 32e71cf42a..94a9821949 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/check_semantic_metadata.rs @@ -7,10 +7,6 @@ use crate::semantic_checks::models::{MetadataOutcome, MetadataRequest, SemanticF use crate::semantic_validation::models::ProjectCatalog; /// Check metadata on the compile's analysis pool. -/// -/// # Errors -/// -/// The error Python's checks raised for the same input, or an internal native failure. pub fn check_semantic_metadata( request: &MetadataRequest, catalog: &ProjectCatalog, diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs index dda6fcd18b..dab3c6d609 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/complete_semantic_diagnostics.rs @@ -7,10 +7,6 @@ use crate::semantic_checks::models::{CompletionOutcome, CompletionRequest, Seman use crate::semantic_validation::models::ProjectCatalog; /// Python's recovery, explanation and opt-out rejection, on the compile's analysis pool. -/// -/// # Errors -/// -/// The error Python's completion raised for the same input, or an internal native failure. pub fn complete_semantic_diagnostics( request: &CompletionRequest, catalog: &ProjectCatalog, diff --git a/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs b/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs index 148adf0931..c3c582e1f8 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/main/plan_type_recovery.rs @@ -7,10 +7,6 @@ use crate::semantic_checks::models::{SemanticFailure, TypeRecoveryRequest, TypeR use crate::semantic_validation::models::ProjectCatalog; /// Plan type recovery on the compile's analysis pool. -/// -/// # Errors -/// -/// The error Python's recovery raised for the same input, or an internal native failure. pub fn plan_type_recovery( request: &TypeRecoveryRequest, catalog: &ProjectCatalog, diff --git a/crates/sqlbuild-analysis/src/semantic_checks/models.rs b/crates/sqlbuild-analysis/src/semantic_checks/models.rs index 9abbe08a66..3ee67376e5 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/models.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/models.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use crate::semantic_checks::types::CompletedParts; -use crate::type_system::models::TypeNormalizationError; +use crate::type_system::errors::TypeNormalizationError; /// One compiled model output column and the upstream columns it reads. #[derive(Clone, Debug, Default)] @@ -61,8 +61,7 @@ pub struct TypeRecoveryRequest { pub diagnostics: Vec, } -/// Why native semantic completion fails: the error Python's completion raised for the same -/// input, or an internal native failure. +/// Why completion fails: Python's error for the same input, or an internal native failure. #[derive(Clone, Debug, Eq, PartialEq)] pub enum SemanticFailure { /// The wheel's `TypeError` for tokenizing without a dialect. diff --git a/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs b/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs index 88faa0d3e6..705a90481b 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/tests/helpers.rs @@ -197,10 +197,10 @@ fn poisoned_chain(dialect: Option<&str>, is_error: bool) -> TypeRecoveryRequest /// Plan and finish type recovery for one case of the poisoned chain. pub(crate) fn recovery_summary(test_case: &TypeRecoveryTestCase) -> RecoverySummary { let request = poisoned_chain(test_case.dialect, test_case.errors); - let (status, step) = match plan_type_recovery(&request, &catalog()) { - Ok(step) => (step.status().to_owned(), step), - Err(failure) => (format!("{failure:?}"), TypeRecoveryStep::Unchanged), - }; + let (status, step) = plan_type_recovery(&request, &catalog()).map_or_else( + |failure| (format!("{failure:?}"), TypeRecoveryStep::Unchanged), + |step| (step.status().to_owned(), step), + ); let revised = vec![ test_case .revised @@ -409,10 +409,10 @@ pub(crate) fn completion_summary(test_case: &CompletionTestCase) -> CompletionSu request.models[2].rejected_opt_out_file = [Some("legacy.sql".to_owned()), None][usize::from(test_case.without_diagnostics)].clone(); let (failure, (diagnostics, model_bindings, order)) = - match complete_semantic_diagnostics(&request, &catalog()) { - Ok(outcome) => (None, outcome.into_parts()), - Err(failure) => (Some(failure), Default::default()), - }; + complete_semantic_diagnostics(&request, &catalog()).map_or_else( + |failure| (Some(failure), Default::default()), + |outcome| (None, outcome.into_parts()), + ); let described_order: Vec = order .into_iter() .map(|entry| described_entry(&request, &diagnostics, entry)) @@ -499,10 +499,10 @@ pub(crate) fn operand_type_summary(test_case: &OperandTypeTestCase) -> Completio .collect(), }; let (failure, (diagnostics, model_bindings, order)) = - match complete_semantic_diagnostics(&request, &catalog()) { - Ok(outcome) => (None, outcome.into_parts()), - Err(failure) => (Some(failure), Default::default()), - }; + complete_semantic_diagnostics(&request, &catalog()).map_or_else( + |failure| (Some(failure), Default::default()), + |outcome| (None, outcome.into_parts()), + ); let described_order: Vec = order .into_iter() .map(|entry| described_entry(&request, &diagnostics, entry)) diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main.rs b/crates/sqlbuild-analysis/src/semantic_validation/main.rs index 6f02155b91..a600f8fa69 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main.rs @@ -2,6 +2,7 @@ pub mod diagnostics; pub mod map_diagnostics; +pub mod new_analysis_pool; pub mod normalize; pub mod normalize_batch; pub mod normalize_dialect; diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/new_analysis_pool.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/new_analysis_pool.rs new file mode 100644 index 0000000000..28f3e3cbc6 --- /dev/null +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/new_analysis_pool.rs @@ -0,0 +1,6 @@ +use crate::semantic_validation::_helpers::catalog; + +/// A deep-stack analysis pool, for work that runs without a compile's catalog. +pub fn new_analysis_pool() -> Result { + catalog::new_analysis_pool() +} diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs index a3ded58057..e37fddd4b9 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs @@ -16,9 +16,8 @@ use crate::type_system::constants::{ MAX_FUNCTION_CALL_DEPTH, POLYGLOT_CUSTOM_TYPE_NAME, POLYGLOT_TYPE_NAME_ALIASES, STRING_TYPE_NAMES, TIMESTAMP_TYPE_NAMES, UNBOUNDED_TEXT_TYPE_NAMES, }; -use crate::type_system::models::{ - NormalizedType, PythonInteger, TypeDialect, TypeFamily, TypeNormalizationError, -}; +use crate::type_system::errors::TypeNormalizationError; +use crate::type_system::models::{NormalizedType, PythonInteger, TypeDialect, TypeFamily}; /// Parse one type with SQLBuild's Polyglot guard; the error text is what Python logs. pub(crate) fn parse_type(type_sql: &str, dialect: &Dialect) -> Result { @@ -179,8 +178,7 @@ fn polyglot_type_name(args: &Map) -> String { data_type } -/// Python's `_polyglot_type_params`: `int(value)` of each present parameter, skipping what -/// `int` rejects. +/// Python's `_polyglot_type_params`: `int(value)` of each parameter, skipping what it rejects. fn polyglot_type_params(args: &Map) -> Vec { let mut params: Vec = Vec::new(); for key in ["precision", "scale", "length"] { diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs index 7df7abd930..0a8533b3da 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/python_text.rs @@ -21,8 +21,7 @@ pub(crate) fn remove_python_space(text: &str) -> String { .collect() } -/// Python's base-10 `int(text)`: whitespace, a sign, Unicode decimal digits and single -/// underscores between digits; None where Python raises `ValueError`. +/// Python's base-10 `int(text)`, None where Python raises `ValueError`. pub(crate) fn python_int(text: &str) -> Option { let python: PythonText = active_python_text(); let stripped: &str = python_strip(text); @@ -51,8 +50,7 @@ pub(crate) fn python_int(text: &str) -> Option { Some(PythonInteger::from_digits(negative, &value)) } -/// Python's `_split_type_and_params`: the regex `^([A-Z0-9_]+)(?:\(([^)]*)\))?$`, then -/// `int(part.strip())` for each comma-separated parameter, skipping what `int` rejects. +/// Python's `_split_type_and_params`. pub(crate) fn split_type_and_params(type_sql: &str) -> (&str, Vec) { let candidate: &str = type_sql.strip_suffix('\n').unwrap_or(type_sql); let name_end: usize = candidate diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs index c993b8418f..411f17c653 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/shallow_args.rs @@ -1,7 +1,4 @@ //! The wheel's `DataType.args` scalars, read without walking nested types. -//! -//! Serializing a whole parsed type recurses once per nesting level; normalization only reads -//! the top-level tag, name and size parameters, so nested values are skipped unvisited. use std::fmt::Display; diff --git a/crates/sqlbuild-analysis/src/type_system/constants.rs b/crates/sqlbuild-analysis/src/type_system/constants.rs index d82184dbdc..39f858f52d 100644 --- a/crates/sqlbuild-analysis/src/type_system/constants.rs +++ b/crates/sqlbuild-analysis/src/type_system/constants.rs @@ -35,3 +35,5 @@ pub(crate) const POLYGLOT_TYPE_NAME_ALIASES: &[(&str, &str)] = &[ ]; /// The function call depth limit SQLBuild passes to every Polyglot parse. pub(crate) const MAX_FUNCTION_CALL_DEPTH: usize = 512; +/// Python's `str(0)`, the only integer text Python treats as false. +pub(crate) const ZERO_TEXT: &str = "0"; diff --git a/crates/sqlbuild-analysis/src/type_system/errors.rs b/crates/sqlbuild-analysis/src/type_system/errors.rs new file mode 100644 index 0000000000..05145a1c60 --- /dev/null +++ b/crates/sqlbuild-analysis/src/type_system/errors.rs @@ -0,0 +1,21 @@ +//! Why type normalization fails. + +/// Why Python's `normalize_type` raises instead of returning a type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TypeNormalizationError { + /// Polyglot does not know the dialect name: the wheel's `ValueError`. + UnknownDialect(String), + /// Polyglot cannot write the parsed type back as SQL: the wheel's `PolyglotError`. + Generation(String), +} + +impl TypeNormalizationError { + /// The message Python's exception carries. + #[must_use] + pub fn message(&self) -> String { + match self { + Self::UnknownDialect(name) => format!("Unknown dialect: {name}"), + Self::Generation(message) => message.clone(), + } + } +} diff --git a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs index e017ba45a4..ef9005288c 100644 --- a/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/main/normalize_type.rs @@ -5,13 +5,10 @@ use polyglot_sql::{Dialect, DialectType}; use crate::type_system::_helpers::dialects::coerce_type_dialect; use crate::type_system::_helpers::fallback::normalize_with_fallback; use crate::type_system::_helpers::parsed::{normalize_parsed, parse_type}; -use crate::type_system::models::{TypeDialect, TypeNormalization, TypeNormalizationError}; +use crate::type_system::errors::TypeNormalizationError; +use crate::type_system::models::{TypeDialect, TypeNormalization}; /// Normalize one type as Python does for the dialect name it hands Polyglot. -/// -/// # Errors -/// -/// Python's exception where it raises: an unknown dialect or an unwritable parsed type. pub fn normalize_type( type_sql: &str, dialect: &str, diff --git a/crates/sqlbuild-analysis/src/type_system/mod.rs b/crates/sqlbuild-analysis/src/type_system/mod.rs index 89ff092ae4..8c83e15b79 100644 --- a/crates/sqlbuild-analysis/src/type_system/mod.rs +++ b/crates/sqlbuild-analysis/src/type_system/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod _helpers; mod constants; +pub mod errors; pub mod main; pub mod models; diff --git a/crates/sqlbuild-analysis/src/type_system/models.rs b/crates/sqlbuild-analysis/src/type_system/models.rs index d420cce531..9497d5bf9f 100644 --- a/crates/sqlbuild-analysis/src/type_system/models.rs +++ b/crates/sqlbuild-analysis/src/type_system/models.rs @@ -1,5 +1,7 @@ //! Normalized type shapes, matching Python's `NormalizedType` and `TypeFamily`. +use crate::type_system::constants::ZERO_TEXT; + /// The semantic family of one normalized type. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TypeFamily { @@ -67,7 +69,7 @@ impl PythonInteger { /// Whether the integer is zero, so Python treats it as false. #[must_use] pub fn is_zero(&self) -> bool { - self.0 == "0" + self.0 == ZERO_TEXT } /// The integer's canonical base-10 text, as Python's `str(value)` spells it. @@ -99,26 +101,6 @@ pub struct NormalizedType { pub length: Option, } -/// Why Python's `normalize_type` raises instead of returning a type. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TypeNormalizationError { - /// Polyglot does not know the dialect name: the wheel's `ValueError`. - UnknownDialect(String), - /// Polyglot cannot write the parsed type back as SQL: the wheel's `PolyglotError`. - Generation(String), -} - -impl TypeNormalizationError { - /// The message Python's exception carries. - #[must_use] - pub fn message(&self) -> String { - match self { - Self::UnknownDialect(name) => format!("Unknown dialect: {name}"), - Self::Generation(message) => message.clone(), - } - } -} - /// A normalized type and the Polyglot parse error Python logs before its text fallback. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TypeNormalization { diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs index 45ea92334b..207359e441 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs @@ -1,5 +1,5 @@ +use crate::type_system::errors::TypeNormalizationError; use crate::type_system::main::normalize_type::normalize_type; -use crate::type_system::models::TypeNormalizationError; use crate::type_system::tests::helpers::normalization_text; use crate::type_system::tests::test_types::{BracketDepthTestCase, NormalizeTypeTestCase}; diff --git a/crates/sqlbuild-core/src/panics/main/native_failure.rs b/crates/sqlbuild-core/src/panics/main/native_failure.rs index 97efa25b2d..9888ceb7ea 100644 --- a/crates/sqlbuild-core/src/panics/main/native_failure.rs +++ b/crates/sqlbuild-core/src/panics/main/native_failure.rs @@ -1,7 +1,6 @@ use crate::constants::NATIVE_FAILURE_PREFIX; -/// An internal native failure the boundary raises as `NativeCompilerError`; a caught panic keeps -/// its own message. +/// An internal native failure the boundary raises as `NativeCompilerError`. pub fn native_failure(context: &str, reason: &str) -> String { if reason.starts_with(NATIVE_FAILURE_PREFIX) { format!("{reason} ({context})") diff --git a/crates/sqlbuild-core/src/text/_helpers/active_text.rs b/crates/sqlbuild-core/src/text/_helpers/active_text.rs new file mode 100644 index 0000000000..a907ea865f --- /dev/null +++ b/crates/sqlbuild-core/src/text/_helpers/active_text.rs @@ -0,0 +1,28 @@ +//! The running Python's string semantics, recorded once per process. + +use std::sync::OnceLock; + +use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; +use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, IGNORECASE_KEY_TABLES, UPPER_TABLES}; +use crate::text::models::{CleandocMargin, PythonText}; + +static ACTIVE: OnceLock = OnceLock::new(); + +/// Record the running Python's semantics; later calls keep the first value. +pub(crate) fn record(python: PythonText) { + let _ = ACTIVE.set(python); +} + +/// The recorded semantics, or Python 3.12's where none were recorded. +pub(crate) fn active() -> PythonText { + *ACTIVE.get_or_init(|| PythonText { + alnum_ranges: ALNUM_TABLES[0].1, + alpha_ranges: ALPHA_TABLES[0].1, + decimal_ranges: DECIMAL_TABLES[0].1, + casefold_mappings: CASEFOLD_TABLES[0].1, + upper_mappings: UPPER_TABLES[0].1, + ignorecase_keys: IGNORECASE_KEY_TABLES[0].1, + cleandoc_margin: CleandocMargin::Whitespace, + minor_version: 12, + }) +} diff --git a/crates/sqlbuild-core/src/text/_helpers/case_tables.rs b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs index bfb489d898..91949421ae 100644 --- a/crates/sqlbuild-core/src/text/_helpers/case_tables.rs +++ b/crates/sqlbuild-core/src/text/_helpers/case_tables.rs @@ -14,8 +14,7 @@ pub(crate) const UPPER_TABLES: [(&str, &[(u32, &str)]); 3] = [ ("16.0.0", include!("../upper_mappings/unicode_16_0_0.in")), ]; -/// `(unicodedata.unidata_version, keys)` of every character whose `re.IGNORECASE` key is not -/// itself. +/// `(unicodedata.unidata_version, keys)` of every character whose ignorecase key differs. pub(crate) const IGNORECASE_KEY_TABLES: [(&str, &[(u32, u32)]); 3] = [ ( "15.0.0", diff --git a/crates/sqlbuild-core/src/text/_helpers/mod.rs b/crates/sqlbuild-core/src/text/_helpers/mod.rs index 017b3bff3e..07c6ab70ae 100644 --- a/crates/sqlbuild-core/src/text/_helpers/mod.rs +++ b/crates/sqlbuild-core/src/text/_helpers/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod active_text; pub(crate) mod alnum_tables; pub(crate) mod case_tables; pub(crate) mod cleandoc; diff --git a/crates/sqlbuild-core/src/text/main/active_python_text.rs b/crates/sqlbuild-core/src/text/main/active_python_text.rs index 2c4ffeb20b..827faa985a 100644 --- a/crates/sqlbuild-core/src/text/main/active_python_text.rs +++ b/crates/sqlbuild-core/src/text/main/active_python_text.rs @@ -1,28 +1,9 @@ //! The running Python's string semantics, set once by the extension module. -use std::sync::OnceLock; - -use crate::text::_helpers::alnum_tables::{ALNUM_TABLES, ALPHA_TABLES, DECIMAL_TABLES}; -use crate::text::_helpers::case_tables::{CASEFOLD_TABLES, IGNORECASE_KEY_TABLES, UPPER_TABLES}; -use crate::text::models::{CleandocMargin, PythonText}; - -static ACTIVE: OnceLock = OnceLock::new(); - -/// Record the running Python's semantics; later calls keep the first value. -pub fn set_active_python_text(python: PythonText) { - let _ = ACTIVE.set(python); -} +use crate::text::_helpers::active_text; +use crate::text::models::PythonText; /// The running Python's semantics; Rust-only callers that never set them get Python 3.12's. pub fn active_python_text() -> PythonText { - *ACTIVE.get_or_init(|| PythonText { - alnum_ranges: ALNUM_TABLES[0].1, - alpha_ranges: ALPHA_TABLES[0].1, - decimal_ranges: DECIMAL_TABLES[0].1, - casefold_mappings: CASEFOLD_TABLES[0].1, - upper_mappings: UPPER_TABLES[0].1, - ignorecase_keys: IGNORECASE_KEY_TABLES[0].1, - cleandoc_margin: CleandocMargin::Whitespace, - minor_version: 12, - }) + active_text::active() } diff --git a/crates/sqlbuild-core/src/text/main/mod.rs b/crates/sqlbuild-core/src/text/main/mod.rs index 1e3aeb5c64..8104f495de 100644 --- a/crates/sqlbuild-core/src/text/main/mod.rs +++ b/crates/sqlbuild-core/src/text/main/mod.rs @@ -13,3 +13,4 @@ pub mod python_ignorecase_key; pub mod python_strip; pub mod python_text; pub mod python_upper; +pub mod set_active_python_text; diff --git a/crates/sqlbuild-core/src/text/main/python_decimal_value.rs b/crates/sqlbuild-core/src/text/main/python_decimal_value.rs index 0bad0311e8..fd866a4d92 100644 --- a/crates/sqlbuild-core/src/text/main/python_decimal_value.rs +++ b/crates/sqlbuild-core/src/text/main/python_decimal_value.rs @@ -3,8 +3,6 @@ use crate::text::models::PythonText; /// The digit value Python's `int()` reads from one character, if `str.isdecimal()` is true. -/// -/// Every generated decimal range starts at a zero digit and runs in blocks of ten. pub fn python_decimal_value(python: PythonText, character: char) -> Option { if character.is_ascii() { return character.to_digit(10); diff --git a/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs b/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs index f5c5a9553e..9e0540fadb 100644 --- a/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs +++ b/crates/sqlbuild-core/src/text/main/python_ignorecase_key.rs @@ -2,17 +2,17 @@ use crate::text::models::PythonText; -/// The key under which `re.IGNORECASE` matches `character`: a pattern character matches a text -/// character exactly when both keys are equal. +/// The key under which `re.IGNORECASE` matches `character`. pub fn python_ignorecase_key(python: PythonText, character: char) -> char { if character.is_ascii() { return character.to_ascii_lowercase(); } let code_point: u32 = u32::from(character); - python + match python .ignorecase_keys .binary_search_by_key(&code_point, |(key, _)| *key) - .ok() - .and_then(|index| char::from_u32(python.ignorecase_keys[index].1)) - .unwrap_or(character) + { + Ok(index) => char::from_u32(python.ignorecase_keys[index].1).unwrap_or(character), + Err(_not_in_table) => character, + } } diff --git a/crates/sqlbuild-core/src/text/main/set_active_python_text.rs b/crates/sqlbuild-core/src/text/main/set_active_python_text.rs new file mode 100644 index 0000000000..efc48dce16 --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/set_active_python_text.rs @@ -0,0 +1,9 @@ +//! Record the running Python's string semantics for native text helpers. + +use crate::text::_helpers::active_text; +use crate::text::models::PythonText; + +/// Record the running Python's semantics; later calls keep the first value. +pub fn set_active_python_text(python: PythonText) { + active_text::record(python); +} diff --git a/crates/sqlbuild-core/src/text/tests/test_python_compat.rs b/crates/sqlbuild-core/src/text/tests/test_python_compat.rs index 1cacc94266..b14e4acbdc 100644 --- a/crates/sqlbuild-core/src/text/tests/test_python_compat.rs +++ b/crates/sqlbuild-core/src/text/tests/test_python_compat.rs @@ -12,7 +12,7 @@ use crate::text::main::python_upper::python_upper; use crate::text::models::PythonText; use crate::text::tests::test_types::{ CaseMappingTestCase, CharacterClassTestCase, CleandocTestCase, CloseMatchesTestCase, - PythonTextTestCase, + IgnorecaseTestCase, PythonTextTestCase, }; const KEYS: &[&str] = &[ @@ -336,30 +336,106 @@ fn given_unicode_versions_when_folding_and_upper_casing_then_python_str_methods_ #[test] fn given_character_pairs_when_matching_ignoring_case_then_python_re_agrees() { - // `re.fullmatch(re.escape(pattern), text, re.IGNORECASE)` under Python 3.12. - let test_cases: [(char, char, bool); 15] = [ - ('k', '\u{212a}', true), - ('K', '\u{212a}', true), - ('s', '\u{17f}', true), - ('i', '\u{131}', true), - ('I', '\u{130}', true), - ('i', '\u{130}', true), - ('\u{3c3}', '\u{3c2}', true), - ('\u{3a3}', '\u{3c2}', true), - ('\u{b5}', '\u{39c}', true), - ('\u{df}', '\u{1e9e}', true), - ('\u{df}', 'S', false), - ('\u{130}', '\u{131}', true), - ('a', '\u{e1}', false), - ('\u{1c80}', '\u{412}', true), - ('\u{fb05}', '\u{fb06}', true), + let test_cases = [ + IgnorecaseTestCase { + description: "k against \u{212a}", + pattern: 'k', + text: '\u{212a}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "K against \u{212a}", + pattern: 'K', + text: '\u{212a}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "s against \u{17f}", + pattern: 's', + text: '\u{17f}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "i against \u{131}", + pattern: 'i', + text: '\u{131}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "I against \u{130}", + pattern: 'I', + text: '\u{130}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "i against \u{130}", + pattern: 'i', + text: '\u{130}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{3c3} against \u{3c2}", + pattern: '\u{3c3}', + text: '\u{3c2}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{3a3} against \u{3c2}", + pattern: '\u{3a3}', + text: '\u{3c2}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{b5} against \u{39c}", + pattern: '\u{b5}', + text: '\u{39c}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{df} against \u{1e9e}", + pattern: '\u{df}', + text: '\u{1e9e}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{df} against S", + pattern: '\u{df}', + text: 'S', + expected_match: false, + }, + IgnorecaseTestCase { + description: "\u{130} against \u{131}", + pattern: '\u{130}', + text: '\u{131}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "a against \u{e1}", + pattern: 'a', + text: '\u{e1}', + expected_match: false, + }, + IgnorecaseTestCase { + description: "\u{1c80} against \u{412}", + pattern: '\u{1c80}', + text: '\u{412}', + expected_match: true, + }, + IgnorecaseTestCase { + description: "\u{fb05} against \u{fb06}", + pattern: '\u{fb05}', + text: '\u{fb06}', + expected_match: true, + }, ]; let python: PythonText = python_text((3, 12), "15.0.0").expect("Python 3.12 is supported"); - for (pattern, text, expected) in test_cases { + for test_case in test_cases { assert_eq!( - python_ignorecase_key(python, pattern) == python_ignorecase_key(python, text), - expected, - "{pattern:?} against {text:?}" + python_ignorecase_key(python, test_case.pattern) + == python_ignorecase_key(python, test_case.text), + test_case.expected_match, + "{}", + test_case.description ); } } diff --git a/crates/sqlbuild-core/src/text/tests/test_types.rs b/crates/sqlbuild-core/src/text/tests/test_types.rs index b5d49dfec2..32828b6c9d 100644 --- a/crates/sqlbuild-core/src/text/tests/test_types.rs +++ b/crates/sqlbuild-core/src/text/tests/test_types.rs @@ -60,3 +60,11 @@ pub(super) struct CaseMappingTestCase { pub(super) expected_casefold: &'static str, pub(super) expected_upper: &'static str, } + +/// `re.fullmatch(re.escape(pattern), text, re.IGNORECASE)` under Python 3.12. +pub(crate) struct IgnorecaseTestCase { + pub(crate) description: &'static str, + pub(crate) pattern: char, + pub(crate) text: char, + pub(crate) expected_match: bool, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs index 2c44a25421..16562c1969 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/nullability_rules.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex, PoisonError}; use pyo3::prelude::{Py, PyAny, PyAnyMethods, PyDictMethods, PyErr, Python}; use pyo3::types::{PyDict, PyTuple}; -use sqlbuild_analysis::assembly::analysis_session::types::NullabilityCallback; +use sqlbuild_analysis::assembly::analysis_session::models::NullabilityCallback; /// The first exception an adapter rule raised, which the session re-raises to Python. pub(crate) type RuleFailure = Arc>>; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs index 0317b0ea58..1aee467db3 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs @@ -5,7 +5,7 @@ use pyo3::types::PyDict; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::main::query_columns::query_columns; use sqlbuild_analysis::assembly::analysis_session::models::{ - QueryColumns, QueryColumnsInput, QueryColumnsMode, QueryColumnsRequest, + ColumnFact, QueryColumns, QueryColumnsInput, QueryColumnsMode, QueryColumnsRequest, }; use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; use sqlbuild_core::panics::main::native_failure::native_failure; @@ -94,12 +94,9 @@ fn query_input(row: QueryRow) -> Result { fn columns_row(columns: QueryColumns) -> ColumnsRow { ( columns.succeeded, - columns.columns.map(|columns| { - columns - .into_iter() - .map(|column| (column.name, column.data_type, column.nullability)) - .collect() - }), + columns + .columns + .map(|columns| columns.into_iter().map(column_row).collect()), columns.has_star, ) } @@ -108,3 +105,7 @@ pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(infer_query_columns, module)?)?; Ok(()) } + +fn column_row(column: ColumnFact) -> (String, Option, String) { + (column.name, column.data_type, column.nullability) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index ac18a79c31..65a1324edc 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -162,8 +162,7 @@ impl NativeModelAnalysisSession { .unwrap_or_default() } - /// Analyse every model; a model's adapter rule exception is raised as it was, any internal - /// native failure as `NativeCompilerError`. + /// Analyse every model. fn run(&mut self, py: Python<'_>) -> PyResult { let mut session: AnalysisSession = self.running()?; let result: Result<(AnalysisSession, SessionStep), String> = @@ -226,18 +225,15 @@ impl NativeModelAnalysisSession { } /// Start a session on `catalog`. -/// -/// `adapter_rules` holds the adapter's own nullability rules by name, with the -/// `InferredNullability` type they take and return; the request names them `python`. #[pyfunction] #[pyo3(signature = (catalog, request, cache=None, adapter_rules=None))] fn start_model_analysis_session( - py: Python<'_>, catalog: PyRef<'_, ProjectCatalog>, request: RequestRow, cache: Option<(PathBuf, String)>, adapter_rules: Option<(Py, Py)>, ) -> PyResult { + let py: Python<'_> = catalog.py(); let mut request: SessionRequest = session_request(request); let rule_failure: RuleFailure = RuleFailure::default(); request.nullability_callback = adapter_rules diff --git a/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs b/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs index 46cea30266..313979971b 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/boundary/python_runtime.rs @@ -1,8 +1,8 @@ //! Record the running Python's string semantics for native text helpers. use pyo3::prelude::{PyAnyMethods, PyResult, Python}; -use sqlbuild_core::text::main::active_python_text::set_active_python_text; use sqlbuild_core::text::main::python_text::python_text; +use sqlbuild_core::text::main::set_active_python_text::set_active_python_text; /// Select the running Python's `str` semantics; an unsupported Python fails at discovery instead. pub(crate) fn record_python_text(python: Python<'_>) -> PyResult<()> { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs index 59cd249c55..7f41ace49c 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/contracts/model_contracts.rs @@ -10,7 +10,7 @@ use sqlbuild_analysis::contracts::models::{ PromotionConflict, PromotionModel, PromotionRequest, }; -use sqlbuild_analysis::type_system::models::TypeNormalizationError; +use sqlbuild_analysis::type_system::errors::TypeNormalizationError; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::_helpers::type_system::normalization::normalization_error; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index f15fd42160..92572c287d 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::types::{PyString, PyStringMethods}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::types::Pairs; use sqlbuild_analysis::assembly::project::main::assemble_project_resources::assemble_project_resources; @@ -100,19 +101,18 @@ fn assemble_project_resource_facts( } } -/// Python's syntax error message for one SQL string, None where it parses; raises where Python -/// raises: `ValueError` for SQL the analysis normalization rejects or an unknown dialect. +/// Python's syntax error message for one SQL string, None where it parses. #[pyfunction] #[pyo3(signature = (sql, placeholders, dialect, parse_one=false))] fn sql_syntax_error( - py: Python<'_>, - sql: String, + sql: &Bound<'_, PyString>, placeholders: Option>, dialect: Option, parse_one: bool, ) -> PyResult> { + let py: Python<'_> = sql.py(); let check = SyntaxCheck { - sql, + sql: sql.to_str()?.to_owned(), placeholders: placeholders.unwrap_or_default().into_iter().collect(), }; let dialect: String = dialect diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs index c296824cb0..4dc2e378c8 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs @@ -10,8 +10,7 @@ const DECLARATION_READ: u8 = 1; const GENERATED_SQL: u8 = 2; const ARGUMENT_REFERENCE: u8 = 3; -/// One call site: start, end, name, tree names if scanned and whether typed reference text -/// appears. +/// One call site: start, end, name, tree names if scanned and whether typed reference text appears. pub(crate) type SiteRow = (usize, usize, String, Option>, bool); /// One substitution span: source start and end, output start and end. pub(crate) type SpanRow = (usize, usize, usize, usize); diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs index 0db21446e8..282fdb56f2 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs @@ -16,6 +16,8 @@ use crate::bindings::_helpers::render::macro_call_memo::MacroCallMemo; use crate::bindings::_helpers::render::macro_call_rows::{SiteRow, SpanRow, site_row, span_row}; use crate::bindings::types::CompilerDetach; +const MACRO_SCAN_CONTEXT: &str = "native macro call scan"; + /// `(call start or None, message)` of the error Python's scan raises. type FailureRow = (Option, &'static str); @@ -42,10 +44,7 @@ pub(crate) fn scan_macro_call_sites( )) } -const MACRO_SCAN_CONTEXT: &str = "native macro call scan"; - -/// Python's `find_macro_call_names` (or with `nested`, `find_nested_macro_call_names`): the -/// names, or the message of the `CompileInputError` Python's scan raises. +/// Python's `find_macro_call_names` names, or the `CompileInputError` message of its scan. #[pyfunction] pub(crate) fn macro_call_names( py: Python<'_>, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs index 43ea7c10a7..8829c70ce4 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs @@ -11,7 +11,9 @@ use sqlbuild_analysis::semantic_checks::models::{ use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::_helpers::semantic_checks::failures::semantic_error; -use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; +use crate::bindings::_helpers::semantic_checks::session_facts::{ + column_names, session_model_facts, +}; use crate::bindings::_helpers::semantic_checks::type_recovery::{LineageInput, lineage_outputs}; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -143,12 +145,7 @@ fn completion_model( ) -> Result { let inferred_columns: Vec = match inferred_columns { Some(names) => names, - None => session_model_facts(session, &name)? - .columns - .iter() - .flatten() - .map(|column| column.name.clone()) - .collect(), + None => column_names(session_model_facts(session, &name)?).unwrap_or_default(), }; let lineage: Vec = match lineage { Some(lineage) => lineage, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs index eaf244fa10..467c2e4eb5 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs @@ -1,7 +1,7 @@ //! Model output names and lineage the semantic stages read from a finished analysis session. use sqlbuild_analysis::assembly::analysis_session::main::finished_model_facts::finished_model_facts; -use sqlbuild_analysis::assembly::analysis_session::models::SessionModelFacts; +use sqlbuild_analysis::assembly::analysis_session::models::{ColumnFact, SessionModelFacts}; use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; @@ -16,3 +16,9 @@ pub(crate) fn session_model_facts<'a>( finished_model_facts(finished, name) .ok_or_else(|| format!("the finished session kept no facts for model {name}")) } + +/// The output column names of a model's session facts. +pub(crate) fn column_names(facts: &SessionModelFacts) -> Option> { + let columns: &[ColumnFact] = facts.columns.as_deref()?; + Some(columns.iter().map(|column| column.name.clone()).collect()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index ade9cdca33..c95f6030ed 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -13,7 +13,9 @@ use sqlbuild_analysis::semantic_checks::types::RevisedBinding; use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::_helpers::semantic_checks::failures::{internal_error, semantic_error}; -use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; +use crate::bindings::_helpers::semantic_checks::session_facts::{ + column_names, session_model_facts, +}; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -141,11 +143,7 @@ fn recovery_model( Some(lineage) => (inferred_columns, lineage), None => { let facts = session_model_facts(session, &name)?; - let names: Option> = facts - .columns - .as_ref() - .map(|columns| columns.iter().map(|column| column.name.clone()).collect()); - (names, facts.lineage.clone()) + (column_names(facts), facts.lineage.clone()) } }; Ok(RecoveryModel { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs index 87cb1ac3a9..5b1c988c04 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs @@ -33,8 +33,7 @@ type FactsRow = ( String, Vec, ); -/// The test's facts, and the `(kind, message)` of the error its assembly raises, if any: kind -/// `input` (no facts), `internal` (no facts) or `decimal_overflow` (after the facts). +/// The test's facts, and the `(kind, message)` of the error its assembly raises, if any. type AssemblyRow = (Option, Option<(&'static str, String)>); /// The Python assembly request: the project's model inputs and the SQL test inputs. diff --git a/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs b/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs index a5c038937a..3f37bbd680 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/type_system/normalization.rs @@ -3,15 +3,13 @@ use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyAnyMethods, PyErr, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::type_system::errors::TypeNormalizationError; use sqlbuild_analysis::type_system::main::normalize_type::normalize_type; -use sqlbuild_analysis::type_system::models::{ - NormalizedType, TypeNormalization, TypeNormalizationError, -}; +use sqlbuild_analysis::type_system::models::{NormalizedType, TypeNormalization}; use crate::bindings::_helpers::boundary::panics::compiler_guard; -/// `(name, family, precision, scale, length)` of one normalized type; the integers are the -/// base-10 text of Python `int`s of any size. +/// `(name, family, precision, scale, length)` of one normalized type. type NormalizedRow = ( String, &'static str, diff --git a/crates/sqlbuild-python/src/bindings/main/register.rs b/crates/sqlbuild-python/src/bindings/main/register.rs index dacbe3588f..5e1929b275 100644 --- a/crates/sqlbuild-python/src/bindings/main/register.rs +++ b/crates/sqlbuild-python/src/bindings/main/register.rs @@ -12,7 +12,6 @@ use crate::bindings::models; #[pymodule] pub fn _native(module: &Bound<'_, PyModule>) -> PyResult<()> { - boundary::python_runtime::record_python_text(module.py())?; rules::evaluation::register(module)?; rules::lint::register(module)?; rules::skills::register(module)?; @@ -42,6 +41,7 @@ pub fn _native(module: &Bound<'_, PyModule>) -> PyResult<()> { } fn register_analysis_stages(module: &Bound<'_, PyModule>) -> PyResult<()> { + boundary::python_runtime::record_python_text(module.py())?; type_system::registration::register(module)?; analysis_session::registration::register(module)?; semantic_checks::registration::register(module)?; diff --git a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs index 542caab55e..c923751ae4 100644 --- a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs +++ b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs @@ -5,7 +5,7 @@ use sqlbuild_core::text::main::is_python_alpha::is_python_alpha; use sqlbuild_core::text::main::is_python_space::is_python_space; use sqlbuild_core::text::models::PythonText; -use crate::macro_calls::models::ScanError; +use crate::macro_calls::errors::ScanError; const TYPED_REFERENCE_NAMES: [&str; 3] = ["__ref", "__source", "__seed"]; @@ -17,8 +17,7 @@ pub(crate) struct ScannedCall { pub(crate) name: String, } -/// Where Python's scan of the text raises: in the call starting at a byte offset, or between -/// calls. +/// Where Python's scan of the text raises: in the call starting at a byte offset, or between calls. pub(crate) struct ScannedFailure { pub(crate) call_start: Option, pub(crate) error: ScanError, @@ -60,8 +59,7 @@ pub(crate) fn top_level_calls( (calls, None) } -/// Python's `_scan_sqlbuild_call_names(include_nested=True)`: every call start's name in order, -/// continuing just past the character after each name, as Python's cursor does. +/// Python's `_scan_sqlbuild_call_names(include_nested=True)`: every call start's name in order. pub(crate) fn every_call_name(python: PythonText, text: &str) -> Result, ScanError> { let mut names: Vec = Vec::new(); let mut cursor = 0; diff --git a/crates/sqlbuild-render/src/macro_calls/errors.rs b/crates/sqlbuild-render/src/macro_calls/errors.rs new file mode 100644 index 0000000000..794df0001c --- /dev/null +++ b/crates/sqlbuild-render/src/macro_calls/errors.rs @@ -0,0 +1,23 @@ +//! The errors Python's macro call scan raises. + +/// An error Python's macro call scan raises. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ScanError { + UnclosedQuote, + UnclosedComment, + UnclosedParenthesis, + /// A call start whose name is not followed by its parenthesis. + MissingParenthesis, +} + +impl ScanError { + /// The message of the `CompileInputError` Python's scan raises. + pub fn message(self) -> &'static str { + match self { + Self::UnclosedQuote => "Macro expansion contains an unclosed quoted string", + Self::UnclosedComment => "Macro expansion contains an unclosed block comment", + Self::UnclosedParenthesis => "Macro expansion contains an unclosed parenthesis", + Self::MissingParenthesis => "expected opening parenthesis", + } + } +} diff --git a/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs b/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs index 60b604432f..d8ee7b0b6a 100644 --- a/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs +++ b/crates/sqlbuild-render/src/macro_calls/main/macro_call_names.rs @@ -3,11 +3,9 @@ use sqlbuild_core::text::models::PythonText; use crate::macro_calls::_helpers::scanner::{every_call_name, top_level_calls}; -use crate::macro_calls::models::ScanError; +use crate::macro_calls::errors::ScanError; -/// Python's `_scan_sqlbuild_call_names`: unique call names in encounter order, declaration -/// calls included; top-level calls only, or with `nested` every call start the scan visits; or -/// the error Python's scan raises. +/// Python's `_scan_sqlbuild_call_names`: unique call names in encounter order. pub fn macro_call_names( python: PythonText, sql: &str, diff --git a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs index d37a520b16..a1b327e59d 100644 --- a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs @@ -8,8 +8,7 @@ use crate::macro_calls::_helpers::scanner::{ }; use crate::macro_calls::models::{MacroCallScan, MacroCallSite, MacroScanFailure}; -/// Every complete top-level call site in order under `python`'s character classes, and where -/// Python's scan raises, if it does. +/// Every complete top-level call site in order, and where Python's scan raises, if it does. pub fn scan_macro_call_sites(python: PythonText, sql: &str) -> MacroCallScan { let (calls, failure) = top_level_calls(python, sql); let mut boundaries: Vec = calls @@ -24,15 +23,7 @@ pub fn scan_macro_call_sites(python: PythonText, sql: &str) -> MacroCallScan { .zip(offsets.as_chunks::<2>().0) .map(|(call, offsets)| { let args = &sql[call.open + 1..call.close]; - let tree_names: Option> = nested_names(python, args).ok().map(|nested| { - let mut tree_names: Vec = vec![call.name.clone()]; - for name in nested { - if !tree_names.contains(&name) { - tree_names.push(name); - } - } - tree_names - }); + let tree_names: Option> = call_tree_names(python, &call.name, args); MacroCallSite { start: offsets[0], end: offsets[1], @@ -50,3 +41,18 @@ pub fn scan_macro_call_sites(python: PythonText, sql: &str) -> MacroCallScan { }), } } + +/// The call's name, then each nested call's once; None where a nested scan raises. +fn call_tree_names(python: PythonText, name: &str, args: &str) -> Option> { + let nested: Vec = match nested_names(python, args) { + Ok(nested) => nested, + Err(_scan_error) => return None, + }; + let mut tree_names: Vec = vec![name.to_owned()]; + for nested_name in nested { + if !tree_names.contains(&nested_name) { + tree_names.push(nested_name); + } + } + Some(tree_names) +} diff --git a/crates/sqlbuild-render/src/macro_calls/mod.rs b/crates/sqlbuild-render/src/macro_calls/mod.rs index e6e5a52eb4..004a682592 100644 --- a/crates/sqlbuild-render/src/macro_calls/mod.rs +++ b/crates/sqlbuild-render/src/macro_calls/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod _helpers; +pub mod errors; pub mod main; pub mod models; diff --git a/crates/sqlbuild-render/src/macro_calls/models.rs b/crates/sqlbuild-render/src/macro_calls/models.rs index 0f8bfa9933..22f0ee1c93 100644 --- a/crates/sqlbuild-render/src/macro_calls/models.rs +++ b/crates/sqlbuild-render/src/macro_calls/models.rs @@ -7,6 +7,8 @@ use sqlbuild_cache::store::models::NativeStore; use std::collections::HashMap; use std::sync::Arc; +use crate::macro_calls::errors::ScanError; + use crate::macro_calls::_helpers::entry_codec::{decode_entry, encode_entry}; /// One top-level `@macro(...)` call in an authored SQL string; offsets count code points. @@ -18,35 +20,12 @@ pub struct MacroCallSite { pub end: usize, /// The called macro's name. pub name: String, - /// Every macro name in the call, the top-level name first, then nested calls once each; - /// None where a nested call is not one Python's scan completes. + /// Every macro name in the call: the top-level name, then nested calls once each. pub tree_names: Option>, /// The arguments mention `__ref`, `__source` or `__seed`, so typed references may be passed. pub typed_reference_text: bool, } -/// An error Python's macro call scan raises. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ScanError { - UnclosedQuote, - UnclosedComment, - UnclosedParenthesis, - /// A call start whose name is not followed by its parenthesis. - MissingParenthesis, -} - -impl ScanError { - /// The message of the `CompileInputError` Python's scan raises. - pub fn message(self) -> &'static str { - match self { - Self::UnclosedQuote => "Macro expansion contains an unclosed quoted string", - Self::UnclosedComment => "Macro expansion contains an unclosed block comment", - Self::UnclosedParenthesis => "Macro expansion contains an unclosed parenthesis", - Self::MissingParenthesis => "expected opening parenthesis", - } - } -} - /// Where a scan stops: inside the call at a code point offset, or between calls. #[derive(Clone, Debug, PartialEq, Eq)] pub struct MacroScanFailure { diff --git a/crates/sqlbuild-render/src/macro_calls/tests/helpers.rs b/crates/sqlbuild-render/src/macro_calls/tests/helpers.rs index 2fa89bc6ec..7766e69df8 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/helpers.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/helpers.rs @@ -3,7 +3,9 @@ use sqlbuild_core::text::main::python_text::python_text; use sqlbuild_core::text::models::PythonText; use std::path::Path; +use crate::macro_calls::errors::ScanError; use crate::macro_calls::models::{MacroCallEntry, MacroCallEvent, MacroCallMemo}; +use crate::macro_calls::tests::test_types::ExpectedNames; /// A memo holding one recorded `@cents(amount)` call in class 1, and that call's entry. pub(crate) fn recorded_memo() -> (MacroCallMemo, MacroCallEntry) { @@ -74,3 +76,8 @@ pub(crate) fn every_event_entry() -> MacroCallEntry { ], } } + +/// Expected call names as the owned names the scan returns. +pub(crate) fn owned_names(names: ExpectedNames) -> Result, ScanError> { + names.map(|names| names.iter().map(|name| (*name).to_owned()).collect()) +} diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs index 7f4106d418..7f2110efd0 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_macro_call_names.rs @@ -1,49 +1,66 @@ +use crate::macro_calls::errors::ScanError; use crate::macro_calls::main::macro_call_names::macro_call_names; -use crate::macro_calls::models::ScanError; -use crate::macro_calls::tests::helpers::python_312; - -/// `(sql, top-level names, nested names)` as Python's `_scan_sqlbuild_call_names` returned them. -type NamesCase = ( - &'static str, - Result<&'static [&'static str], ScanError>, - Result<&'static [&'static str], ScanError>, -); +use crate::macro_calls::tests::helpers::{owned_names, python_312}; +use crate::macro_calls::tests::test_types::MacroCallNamesTestCase; #[test] fn given_sql_when_listing_macro_call_names_then_python_scan_agrees() { - let test_cases: [NamesCase; 7] = [ - ("@enum(x) @a(1)", Ok(&["enum", "a"]), Ok(&["enum", "a"])), - ("@ab c(1)", Err(ScanError::MissingParenthesis), Ok(&["ab"])), - ("x@\u{f6}re FROM o", Ok(&[]), Ok(&[])), - ( - "@\u{e9}(1) @b(", - Err(ScanError::UnclosedParenthesis), - Ok(&["\u{e9}", "b"]), - ), - ("'@q()' /* @c() */ @d ( 1 ) @@v", Ok(&[]), Ok(&[])), - ("@a(@b(@c()))", Ok(&["a"]), Ok(&["a", "b", "c"])), - ( - "@a(\"x\") 'oops", - Err(ScanError::UnclosedQuote), - Err(ScanError::UnclosedQuote), - ), + let test_cases = [ + MacroCallNamesTestCase { + description: "declaration calls are listed", + sql: "@enum(x) @a(1)", + expected_top_level: Ok(&["enum", "a"]), + expected_nested: Ok(&["enum", "a"]), + }, + MacroCallNamesTestCase { + description: "a name without its parenthesis fails only the top-level scan", + sql: "@ab c(1)", + expected_top_level: Err(ScanError::MissingParenthesis), + expected_nested: Ok(&["ab"]), + }, + MacroCallNamesTestCase { + description: "an at sign inside a word starts no call", + sql: "x@\u{f6}re FROM o", + expected_top_level: Ok(&[]), + expected_nested: Ok(&[]), + }, + MacroCallNamesTestCase { + description: "an unclosed call fails only the top-level scan", + sql: "@\u{e9}(1) @b(", + expected_top_level: Err(ScanError::UnclosedParenthesis), + expected_nested: Ok(&["\u{e9}", "b"]), + }, + MacroCallNamesTestCase { + description: "quoted, commented and doubled at signs start no call", + sql: "'@q()' /* @c() */ @d ( 1 ) @@v", + expected_top_level: Ok(&[]), + expected_nested: Ok(&[]), + }, + MacroCallNamesTestCase { + description: "nested calls are listed only when nested", + sql: "@a(@b(@c()))", + expected_top_level: Ok(&["a"]), + expected_nested: Ok(&["a", "b", "c"]), + }, + MacroCallNamesTestCase { + description: "an unclosed quote fails both scans", + sql: "@a(\"x\") 'oops", + expected_top_level: Err(ScanError::UnclosedQuote), + expected_nested: Err(ScanError::UnclosedQuote), + }, ]; - for (sql, top_level, nested) in test_cases { - let owned = |names: Result<&'static [&'static str], ScanError>| { - names.map(|names| { - names - .iter() - .map(|name| (*name).to_owned()) - .collect::>() - }) - }; + for test_case in test_cases { assert_eq!( ( - macro_call_names(python_312(), sql, false), - macro_call_names(python_312(), sql, true) + macro_call_names(python_312(), test_case.sql, false), + macro_call_names(python_312(), test_case.sql, true) + ), + ( + owned_names(test_case.expected_top_level), + owned_names(test_case.expected_nested) ), - (owned(top_level), owned(nested)), - "{sql}" + "{}", + test_case.description ); } } diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs index 9d03816e34..7199def831 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs @@ -1,5 +1,5 @@ +use crate::macro_calls::errors::ScanError; use crate::macro_calls::main::scan_macro_call_sites::scan_macro_call_sites; -use crate::macro_calls::models::ScanError; use crate::macro_calls::tests::helpers::python_312; use std::time::Instant; diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs index b782de67f7..88311fbbcf 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs @@ -1,7 +1,6 @@ -use crate::macro_calls::models::ScanError; +use crate::macro_calls::errors::ScanError; -/// One expected call site: start, end, name, tree names if scanned and whether typed reference -/// text appears. +/// One expected call site: start, end, name, scanned tree names and typed reference text. pub(crate) type ExpectedSite = ( usize, usize, @@ -49,3 +48,13 @@ pub(crate) struct DeepNestingTestCase { pub(crate) expected_tree_names: &'static [&'static str], pub(crate) expected_max_seconds: f64, } + +/// Call names as Python's `_scan_sqlbuild_call_names` returned them, or its scan error. +pub(crate) type ExpectedNames = Result<&'static [&'static str], ScanError>; + +pub(crate) struct MacroCallNamesTestCase { + pub(crate) description: &'static str, + pub(crate) sql: &'static str, + pub(crate) expected_top_level: ExpectedNames, + pub(crate) expected_nested: ExpectedNames, +} diff --git a/crates/sqlbuild-sqltext/src/compiler/main/mod.rs b/crates/sqlbuild-sqltext/src/compiler/main/mod.rs index f488f56683..7b066a290e 100644 --- a/crates/sqlbuild-sqltext/src/compiler/main/mod.rs +++ b/crates/sqlbuild-sqltext/src/compiler/main/mod.rs @@ -5,5 +5,6 @@ pub mod model_header_matching; pub mod model_header_parsing; pub mod model_header_single_parsing; pub mod model_header_tokenizing; +pub mod placeholder_defaults; pub mod sql_interpolation; pub mod statement_header_matching; diff --git a/crates/sqlbuild-sqltext/src/compiler/main/placeholder_defaults.rs b/crates/sqlbuild-sqltext/src/compiler/main/placeholder_defaults.rs new file mode 100644 index 0000000000..cb69cf220a --- /dev/null +++ b/crates/sqlbuild-sqltext/src/compiler/main/placeholder_defaults.rs @@ -0,0 +1,43 @@ +//! Python's `substitute_placeholder_defaults`. + +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_word::is_python_word; + +use crate::constants::PLACEHOLDER_PREFIX; + +/// `substitute_placeholder_defaults`: `@@@name` becomes its default; unknown names stay. +pub fn placeholder_defaults(sql: &str, placeholders: &[(String, String)]) -> String { + let mut result: String = String::with_capacity(sql.len()); + let mut rest: &str = sql; + while let Some(start) = rest.find(PLACEHOLDER_PREFIX) { + result.push_str(&rest[..start]); + let after: &str = &rest[start + PLACEHOLDER_PREFIX.len()..]; + let length: usize = after + .find(|character: char| !is_word(character)) + .unwrap_or(after.len()); + if length == 0 { + result.push_str(&rest[start..=start]); + rest = &rest[start + 1..]; + continue; + } + let name: &str = &after[..length]; + match placeholders + .iter() + .find(|(placeholder, _)| placeholder == name) + { + Some((_, default)) => result.push_str(default), + None => { + result.push_str(PLACEHOLDER_PREFIX); + result.push_str(name); + } + } + rest = &after[length..]; + } + result.push_str(rest); + result +} + +/// A character Python's `\w` matches. +fn is_word(character: char) -> bool { + is_python_word(active_python_text(), character) +} diff --git a/crates/sqlbuild-sqltext/src/constants.rs b/crates/sqlbuild-sqltext/src/constants.rs index f1e65ca5bb..775734cfba 100644 --- a/crates/sqlbuild-sqltext/src/constants.rs +++ b/crates/sqlbuild-sqltext/src/constants.rs @@ -7,3 +7,5 @@ pub(crate) const OUTSIDE_KEY: &str = "outside"; pub(crate) const SQL_HOOK: &str = "sql"; pub(crate) const THRESHOLDS_KEY: &str = "thresholds"; pub(crate) const TYPE_KEY: &str = "type"; +/// The prefix that opens a SQL placeholder. +pub(crate) const PLACEHOLDER_PREFIX: &str = "@@@"; diff --git a/fensu.toml b/fensu.toml index a10fe5c399..d2a0dd91ec 100644 --- a/fensu.toml +++ b/fensu.toml @@ -280,6 +280,16 @@ rule = "FPRSS201" path = "crates/sqlbuild-python/src/bindings/models.rs" reason = "CompactAnalysisJob holds a prepared analysis closure, which cannot implement Debug; other models derive it." +[[targets.rust.rule_exceptions]] +rule = "FPRSS130" +path = "crates/sqlbuild-core/src/text/_helpers/active_text.rs" +reason = "One interpreter per process: the extension module records the running Python's string semantics once at import, and every native text helper reads that single value." + +[[targets.rust.rule_exceptions]] +rule = "FPRSS201" +path = "crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs" +reason = "NullabilityCallback implements Debug manually because it holds the adapter's nullability rule closure, which cannot implement Debug; other models derive it." + [[targets.rust.rule_exceptions]] rule = "FPRSL105" path = "crates/sqlbuild-analysis/src/sql_tokens/main/builtin_function_names.rs" diff --git a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py index 4bfc505a39..b51f3e970a 100644 --- a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py +++ b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py @@ -16,12 +16,14 @@ from scripts.compiler_differential._helpers.running.execution import run_engine from scripts.compiler_differential.constants import ( ANALYSIS_CODE_SCAN_ROOTS, + ANALYSIS_NATIVE_CODE_SCAN_ROOTS, COLD_COMPILE, CORPUS_FAILURES, DIAGNOSTIC_CODE_PATTERN, ERROR_SEVERITY, EXPECT_SUCCESS, LEFT_SIDE, + NATIVE_CODE_CONSTANT_PATTERN, RENDER_CODE_SCAN_EXCLUDED, RENDER_RAISED_CODES, WARNING_SEVERITY, @@ -113,13 +115,17 @@ def render_error_codes() -> frozenset[str]: def analysis_error_codes(compiler_root: Path) -> frozenset[str]: - """Return the help-catalogue codes plus every code literal in the analysis-stage modules.""" + """Return the help-catalogue codes plus every code in the analysis-stage modules and crates.""" found: set[str] = set(semantic_help_catalogue()) for root in ANALYSIS_CODE_SCAN_ROOTS: path: Path = compiler_root / root for module in sorted(path.rglob("*.py")) if path.is_dir() else (path,): found.update(_module_codes(module)) + repo_root: Path = compiler_root.parents[2] + for native_module in ANALYSIS_NATIVE_CODE_SCAN_ROOTS: + text: str = (repo_root / native_module).read_text(encoding="utf-8") + found.update(NATIVE_CODE_CONSTANT_PATTERN.findall(text)) return frozenset(found) diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index 17420f0efd..80a567e030 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -685,6 +685,12 @@ def short_model_names(*, model: Model, ctx: RuleContext) -> list[Finding]: "pipeline/_helpers/target_validation.py", "sql_analysis", ) +ANALYSIS_NATIVE_CODE_SCAN_ROOTS: tuple[str, ...] = ( + "crates/sqlbuild-analysis/src/contracts/constants.rs", +) +NATIVE_CODE_CONSTANT_PATTERN: re.Pattern[str] = re.compile( + r'const [A-Z0-9_]*_CODE: &str = "([A-Z][0-9]{3})";' +) _NEVER_REPORTED_BY_POLYGLOT: str = ( "polyglot defines the finding but never reports it, and no SQLBuild check maps to it" ) diff --git a/scripts/python_char_table/_helpers/table.py b/scripts/python_char_table/_helpers/table.py index d7608e657d..e9049857f9 100644 --- a/scripts/python_char_table/_helpers/table.py +++ b/scripts/python_char_table/_helpers/table.py @@ -70,12 +70,7 @@ def ignorecase_key_path() -> Path: def ignorecase_keys() -> list[tuple[int, int]]: - """Return every character whose `re.IGNORECASE` match key is not itself, with that key. - - A pattern character matches a text character under `re.IGNORECASE` exactly when both have - the same key: the smallest of the character's `_sre` lower case and its `re._casefix` - equivalents. - """ + """Return each character whose `re.IGNORECASE` key (least `_sre` lower/casefix) differs.""" sre: Any = importlib.import_module("_sre") extra: dict[int, tuple[int, ...]] = importlib.import_module("re._casefix")._EXTRA_CASES # noqa: SLF001 diff --git a/src/sqlbuild/adapter/type_system/main/_safe_cast_nullability.py b/src/sqlbuild/adapter/type_system/main/safe_cast_nullability.py similarity index 100% rename from src/sqlbuild/adapter/type_system/main/_safe_cast_nullability.py rename to src/sqlbuild/adapter/type_system/main/safe_cast_nullability.py diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py index cce90227a3..e4cb8eb2a5 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/profile_rows.py @@ -6,11 +6,11 @@ from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapter.contract.types import FunctionNullabilityRule -from sqlbuild.adapter.type_system.main._safe_cast_nullability import safe_cast_nullability from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( conditional_result_nullability, ) from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability +from sqlbuild.adapter.type_system.main.safe_cast_nullability import safe_cast_nullability from sqlbuild.compiler.analysis_session.constants import ( NULLABILITY_RULE_ADAPTER, NULLABILITY_RULE_CONDITIONAL_RESULT, @@ -57,7 +57,8 @@ def case_sensitive_shapes(*, profile: ExpressionInferenceProfile, dialect: str | def shape_rows(shapes: Mapping[str, Mapping[str, str]]) -> ShapeRows: """`{relation: {column: value}}` as ordered rows; discovery rejects non-text names and types.""" - return [ - (name, [(column, str(value)) for column, value in shape.items()]) - for name, shape in shapes.items() - ] + return [(name, _shape_pairs(shape)) for name, shape in shapes.items()] + + +def _shape_pairs(shape: Mapping[str, str]) -> list[tuple[str, str]]: + return [(column, str(value)) for column, value in shape.items()] diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py index c9040d58ca..3f05efbd63 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py @@ -16,8 +16,8 @@ ) from sqlbuild.compiler.analysis_session.constants import ADAPTER_NULLABILITY_CALLBACK from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns -from sqlbuild.compiler.compile._helpers.sharing.binding import lineage_reference_map from sqlbuild.compiler.compile.models import CompileSqlReference, InferredColumn +from sqlbuild.compiler.compile.types import CompiledResourceType from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.constants import ( @@ -29,6 +29,12 @@ _BATCH_MODE: str = "batch" _NORMALIZED_MODES: frozenset[str] = frozenset({"parse", "legacy"}) +_LINEAGE_RESOURCE_TYPES: dict[str, CompiledResourceType] = { + SqlReferenceKind.REF: CompiledResourceType.MODEL, + SqlReferenceKind.SOURCE: CompiledResourceType.SOURCE, + SqlReferenceKind.SEED: CompiledResourceType.SEED, + SqlReferenceKind.TABLE_FUNCTION: CompiledResourceType.TABLE_FN, +} def native_query_columns( @@ -81,22 +87,22 @@ def native_query_columns( return tuple( NativeQueryColumns( succeeded=succeeded, - columns=( - None - if columns is None - else tuple( - InferredColumn( - name=name, type=data_type, nullability=InferredNullability(nullability) - ) - for name, data_type, nullability in columns - ) - ), + columns=None if columns is None else _inferred_columns(columns), has_star=has_star, ) for succeeded, columns, has_star in rows ) +def _inferred_columns( + columns: list[tuple[str, str | None, str]], +) -> tuple[InferredColumn, ...]: + return tuple( + InferredColumn(name=name, type=data_type, nullability=InferredNullability(nullability)) + for name, data_type, nullability in columns + ) + + def _query_row(*, query: NativeColumnQuery, dialect: str | None) -> tuple[object, ...]: references: tuple[CompileSqlReference, ...] = query.references return ( @@ -106,14 +112,29 @@ def _query_row(*, query: NativeColumnQuery, dialect: str | None) -> tuple[object list((query.placeholders or {}).items()), [_analysis_name(reference) for reference in references], [ - (name, resource_type.value, resource_name) - for name, (resource_type, resource_name) in lineage_reference_map(references).items() + (name, resource_type, resource_name) + for name, (resource_type, resource_name) in _lineage_references(references).items() ], query.recover_cte_facts, query.mode, ) +def _lineage_references( + references: tuple[CompileSqlReference, ...], +) -> dict[str, tuple[str, str]]: + """Each lineage reference's analysis name, resource type and name; the last wins in place.""" + + return { + _analysis_name(reference): ( + _LINEAGE_RESOURCE_TYPES[reference.ref_kind].value, + reference.ref_name, + ) + for reference in references + if reference.ref_kind in _LINEAGE_RESOURCE_TYPES + } + + def _analysis_name(reference: CompileSqlReference) -> str: if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: return f"{TABLE_FUNCTION_ANALYSIS_PREFIX}{reference.ref_name}" diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py index 1d713d446d..6a3fdff5fd 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/metadata_validation.py @@ -204,12 +204,15 @@ def _sql_test_columns( profile=profile, ) result: dict[str, tuple[str, ...]] = { - sql: tuple(column.name for column in analysis.columns or ()) - for sql, analysis in zip(queries, analyses, strict=True) + sql: _column_names(analysis) for sql, analysis in zip(queries, analyses, strict=True) } return result +def _column_names(analysis: NativeQueryColumns) -> tuple[str, ...]: + return tuple(column.name for column in analysis.columns or ()) + + def _names(value: object) -> tuple[str, ...]: if isinstance(value, str): return (value,) diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index 3503419b04..084fac16d6 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -20,9 +20,8 @@ MacroExpansionState, ) from sqlbuild.compiler.frontier.exceptions import NativeStageMismatchError -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite -from sqlbuild.compiler.macro_bridge._helpers.call_sites import scan_macro_calls, splice_macro_calls from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( ARGUMENT_REFERENCE_EVENT, @@ -30,6 +29,8 @@ GENERATED_SQL_EVENT, MACRO_USE_EVENT, ) +from sqlbuild.compiler.macro_bridge.main._scan_macro_calls import scan_macro_calls +from sqlbuild.compiler.macro_bridge.main._splice_macro_calls import splice_macro_calls from sqlbuild.compiler.macro_bridge.models import ( MacroCallClass, MacroCallScan, @@ -50,12 +51,7 @@ def expand_bridged_sql_macros( state: MacroExpansionState, bridge: MacroBridge, ) -> tuple[str, tuple[ExpansionSpan, ...]]: - """Expand one string, replaying memoized calls and running the rest in Python in order. - - Where a call's macro tree does not resolve, or the scan raises, no call of the string touches - the memo: each is evaluated in order, so errors and side effects keep the plain expansion's - order. - """ + """Expand one string, replaying memoized calls; unresolved trees evaluate calls in order.""" if MACRO_TOKEN not in sql: return sql, () @@ -181,7 +177,7 @@ def expand_unbridged_sql_macros( consumer_path=consumer_path, state=state, declarations=_expansion_declarations(state=state, consumer_path=consumer_path), - scan=scan_macro_calls(sql), + scan=scan_macro_calls(sql=sql), ) diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 3224c5b85c..da8311dd71 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -26,7 +26,7 @@ DynamicColumnContractProof, ModelSqlAnalysis, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.project_assembly.main._assemble_native_project_resources import ( diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py index 554011fb02..0f257e5a5f 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py @@ -16,7 +16,7 @@ CompileProjectInputs, CompileSqlTestInput, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.sql_test_glue.constants import ( SQL_TEST_DECIMAL_OVERFLOW, @@ -34,8 +34,7 @@ def assemble_sql_tests_by_engine( inputs: CompileProjectInputs, macro_mock_queries: Callable[[CompileSqlTestInput], dict[str, str]], ) -> tuple[CompiledSqlTest, ...]: - """Assemble every SQL test natively; a test mocking macros re-expands each model's query - with the user's macros, and errors raise in Python's assembly order.""" + """Assemble every SQL test natively, raising errors in Python's assembly order.""" native: tuple[NativeSqlTestAssembly, ...] = assemble_native_sql_tests(inputs=inputs) tests: list[CompiledSqlTest] = [] diff --git a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py index 27771f9300..1106e3bf14 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py @@ -32,7 +32,7 @@ EnumMember, ModelSchemaDeclaration, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.model_loop.constants import ( ENUM_REFERENCE_KIND_CODE, diff --git a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py index 7b2cc10029..2f7bd57eca 100644 --- a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py +++ b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py @@ -11,7 +11,7 @@ ExpansionSpan, SqlExpansionContext, ) -from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite diff --git a/src/sqlbuild/compiler/discovery/main/_require_supported_python.py b/src/sqlbuild/compiler/discovery/main/_require_supported_python.py new file mode 100644 index 0000000000..00332f5cb7 --- /dev/null +++ b/src/sqlbuild/compiler/discovery/main/_require_supported_python.py @@ -0,0 +1,14 @@ +"""Require a Python whose string semantics the native extension reproduces.""" + +from __future__ import annotations + +from typing import cast + +from sqlbuild.compiler.discovery._helpers.native.payloads import native_text_runtime + + +def require_supported_python() -> tuple[int, int]: + """The running Python's version; raises `UnsupportedPythonError` where native lacks its text.""" + + runtime: dict[str, object] = native_text_runtime() + return cast(tuple[int, int], runtime["python_version"]) diff --git a/src/sqlbuild/compiler/frontier/main/report_native_fallback.py b/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py similarity index 100% rename from src/sqlbuild/compiler/frontier/main/report_native_fallback.py rename to src/sqlbuild/compiler/frontier/main/_report_native_fallback.py diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index 5438915f3e..1e630cb98c 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -30,8 +30,7 @@ class CompilerStage(StrEnum): class NativeFallbackSite(StrEnum): - """A place where a shipped native stage still hands its work to the Python implementation, - or runs a user's Python extension such as a macro.""" + """A place where native hands work to Python: a fallback or a user's extension callback.""" SCOPE_REBIND_LOOKUP = "declaration_scopes.rebind_lookup" LINT_EXPANSION = "model_loop.lint_expansion" diff --git a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py index c59abdc798..7f66a866c6 100644 --- a/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py +++ b/src/sqlbuild/compiler/lineage/_helpers/native_fast_columns.py @@ -19,6 +19,7 @@ NATIVE_LINEAGE_UNKNOWN_DIALECT, NATIVE_LINEAGE_UNPARSED, ) +from sqlbuild.compiler.lineage.exceptions import UnknownLineageDialectError from sqlbuild.compiler.lineage.models import ( ColumnLineage, ColumnLineageSource, @@ -95,7 +96,7 @@ def build_native_fast_project_column_lineage( sqlbuild_error=detail, ) elif status == NATIVE_LINEAGE_UNKNOWN_DIALECT: - raise ValueError(f"Unknown dialect: {detail}") + raise UnknownLineageDialectError(f"Unknown dialect: {detail}") if result is None: continue model_results[model.name] = result diff --git a/src/sqlbuild/compiler/lineage/exceptions.py b/src/sqlbuild/compiler/lineage/exceptions.py index 100f2aaab3..20dee9b906 100644 --- a/src/sqlbuild/compiler/lineage/exceptions.py +++ b/src/sqlbuild/compiler/lineage/exceptions.py @@ -3,3 +3,7 @@ class LineageAnalysisError(RuntimeError): """Raised when the native lineage boundary returns an invalid payload.""" + + +class UnknownLineageDialectError(ValueError): + """Raised, as Python's parser raised `ValueError`, for a dialect Polyglot does not know.""" diff --git a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py index 3c09bbe060..0351f995f6 100644 --- a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py @@ -14,7 +14,6 @@ LoadedMacro, MacroContext, ) -from sqlbuild.compiler.macro_bridge._helpers.call_sites import scan_macro_calls, splice_macro_calls from sqlbuild.compiler.macro_bridge._helpers.store_environment import ( digest_module_files, module_digests_metadata, @@ -29,6 +28,8 @@ macro_store_token, ) from sqlbuild.compiler.macro_bridge.constants import MACRO_CALL_STORE_FILE_NAME +from sqlbuild.compiler.macro_bridge.main._scan_macro_calls import scan_macro_calls +from sqlbuild.compiler.macro_bridge.main._splice_macro_calls import splice_macro_calls from sqlbuild.compiler.macro_bridge.models import ( MacroCallClass, MacroCallScan, @@ -140,7 +141,7 @@ def scan(self, sql: str) -> MacroCallScan: """The complete top-level call sites of `sql`, and where its scan raises, if it does.""" return scan_macro_calls( - sql, python_version=self._python_version, unicode_version=self._unicode_version + sql=sql, python_version=self._python_version, unicode_version=self._unicode_version ) def call_class( diff --git a/src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py b/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py similarity index 60% rename from src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py rename to src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py index ab305f7354..5d704f0da9 100644 --- a/src/sqlbuild/compiler/macro_bridge/_helpers/call_sites.py +++ b/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py @@ -1,4 +1,4 @@ -"""Native macro call-site scans and splices of one authored string.""" +"""Scan one authored string's top-level macro call sites natively.""" from __future__ import annotations @@ -6,13 +6,12 @@ import unicodedata import sqlbuild._native as _native -from sqlbuild.compiler.compile.models import ExpansionSpan from sqlbuild.compiler.macro_bridge.models import MacroCallScan, MacroCallSite, MacroScanFailure def scan_macro_calls( - sql: str, *, + sql: str, python_version: tuple[int, int] | None = None, unicode_version: str | None = None, ) -> MacroCallScan: @@ -40,24 +39,3 @@ def scan_macro_calls( if failure is None else MacroScanFailure(call_start=failure[0], message=failure[1]), ) - - -def splice_macro_calls( - *, sql: str, sites: tuple[MacroCallSite, ...], outputs: list[str] -) -> tuple[str, tuple[ExpansionSpan, ...]]: - """Replace each call site with its output, returning the SQL and substitution spans.""" - - rendered: str - spans: list[tuple[int, int, int, int]] - rendered, spans = _native.splice_macro_calls( - sql, [(site.start, site.end) for site in sites], outputs - ) - return rendered, tuple( - ExpansionSpan( - source_start=source_start, - source_end=source_end, - output_start=output_start, - output_end=output_end, - ) - for source_start, source_end, output_start, output_end in spans - ) diff --git a/src/sqlbuild/compiler/macro_bridge/main/_splice_macro_calls.py b/src/sqlbuild/compiler/macro_bridge/main/_splice_macro_calls.py new file mode 100644 index 0000000000..480f4ef956 --- /dev/null +++ b/src/sqlbuild/compiler/macro_bridge/main/_splice_macro_calls.py @@ -0,0 +1,28 @@ +"""Splice macro call outputs into one authored string natively.""" + +from __future__ import annotations + +import sqlbuild._native as _native +from sqlbuild.compiler.compile.models import ExpansionSpan +from sqlbuild.compiler.macro_bridge.models import MacroCallSite + + +def splice_macro_calls( + *, sql: str, sites: tuple[MacroCallSite, ...], outputs: list[str] +) -> tuple[str, tuple[ExpansionSpan, ...]]: + """Replace each call site with its output, returning the SQL and substitution spans.""" + + rendered: str + spans: list[tuple[int, int, int, int]] + rendered, spans = _native.splice_macro_calls( + sql, [(site.start, site.end) for site in sites], outputs + ) + return rendered, tuple( + ExpansionSpan( + source_start=source_start, + source_end=source_end, + output_start=output_start, + output_end=output_end, + ) + for source_start, source_end, output_start, output_end in spans + ) diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index 201058d9f5..3eb1b28d59 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -3,12 +3,11 @@ from __future__ import annotations import logging -import sys import unicodedata from collections.abc import Callable from contextvars import Token -from sqlbuild.compiler.discovery._helpers.native.payloads import native_text_runtime +from sqlbuild.compiler.discovery.main._require_supported_python import require_supported_python from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS @@ -16,8 +15,7 @@ def run_with_macro_bridge[T](*, stage: Callable[[], T]) -> T: """Run `stage` once with the bridge; its errors are the Python stage's, raised as they occur.""" - python_version: tuple[int, int] = (sys.version_info[0], sys.version_info[1]) - _ = native_text_runtime() + python_version: tuple[int, int] = require_supported_python() bridge: MacroBridge = MacroBridge( python_version=python_version, unicode_version=unicodedata.unidata_version ) diff --git a/src/sqlbuild/compiler/sql_test_glue/models.py b/src/sqlbuild/compiler/sql_test_glue/models.py index 5e384c5579..69d1bdc130 100644 --- a/src/sqlbuild/compiler/sql_test_glue/models.py +++ b/src/sqlbuild/compiler/sql_test_glue/models.py @@ -60,8 +60,7 @@ class NativeSqlTestFailure: @dataclass(frozen=True) class NativeSqlTestAssembly: - """One natively assembled test, the keyed diagnostics to report before it, and the error its - assembly raises: before the test where it has none, else after its macro-mock queries.""" + """One natively assembled test, its keyed diagnostics, and the error its assembly raises.""" test: CompiledSqlTest | None diagnostics: tuple[tuple[tuple[str, ...], CompilerDiagnostic], ...] diff --git a/src/sqlbuild/sql_values/main/identity.py b/src/sqlbuild/sql_values/main/identity.py deleted file mode 100644 index 8ba2ccddff..0000000000 --- a/src/sqlbuild/sql_values/main/identity.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Public entry point for typed SQL value identity.""" - -from sqlbuild.sql_values._helpers.normalization import sql_value_identity as _sql_value_identity -from sqlbuild.sql_values.models import SqlValue - - -def sql_value_identity(*, value: SqlValue) -> tuple[object, ...]: - """Return the normalized typed identity of a SQL value.""" - - return _sql_value_identity(value=value) diff --git a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py index 25337fbea2..fba51cf622 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/helpers.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/helpers.py @@ -209,3 +209,12 @@ def emit(self, record: logging.LogRecord) -> None: getattr(record, "sqlbuild_error", None), ) ) + + +def dialect_outcomes(outcomes: list[TypeOutcome]) -> dict[str, tuple[object, bool]]: + """One type string's normalization and whether it logged errors, by dialect.""" + + return { + str(outcome.dialect): (outcome.normalized, bool(outcome.logged_errors)) + for outcome in outcomes + } diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py index 874eb28c69..6df3e97bc5 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_normalization.py @@ -25,13 +25,14 @@ from tests.integration.src.sqlbuild.adapter.type_system.helpers import ( DIALECTS, TypeOutcome, + dialect_outcomes, generated_type, is_normalized, logged_parse_error, type_outcome, type_outcomes, ) -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, @@ -63,11 +64,7 @@ def test_given_generated_types_when_normalizing_then_every_known_dialect_answers normalized: list[TypeOutcome] = list(filter(is_normalized, outcomes)) golden: list[GoldenEntry] = [ golden_entry( - "type", - { - str(outcome.dialect): (outcome.normalized, bool(outcome.logged_errors)) - for outcome in outcomes[index * len(DIALECTS) : (index + 1) * len(DIALECTS)] - }, + "type", dialect_outcomes(outcomes[index * len(DIALECTS) : (index + 1) * len(DIALECTS)]) ) for index in range(len(type_strings)) ] diff --git a/tests/integration/src/sqlbuild/cli/commands/main/helpers.py b/tests/integration/src/sqlbuild/cli/commands/main/helpers.py index bdc073dcf6..e5910cb01d 100644 --- a/tests/integration/src/sqlbuild/cli/commands/main/helpers.py +++ b/tests/integration/src/sqlbuild/cli/commands/main/helpers.py @@ -174,7 +174,6 @@ def fail_native_planner(monkeypatch: pytest.MonkeyPatch) -> None: def fail(**kwargs: object) -> tuple[NativeSqlTestArtifact, ...]: raise NativeSqlTestPlanningError("native SQL-test planning failed: worker stopped") - monkeypatch.setattr(target_writer, "plan_and_render_sql_test_artifacts", fail) monkeypatch.setattr(target_writer, "plan_native_sql_test_artifacts", fail) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 8afd00268b..4e075238ee 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -118,7 +118,7 @@ class FallbackColumnsTestCase: description: str query_sql: str - expected_columns: tuple[tuple[str, str | None, str], ...] | None + expected_columns: tuple[tuple[str, str | None, str], ...] @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index d7ec7a7e89..3b91bd7dd2 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -1,5 +1,7 @@ from __future__ import annotations +import functools +import itertools import json import random from collections import Counter @@ -51,6 +53,7 @@ CompileAdapterContext, CompiledLineageColumnFact, CompiledModel, + CompileModelInput, CompileProjectInputs, DynamicColumnContractProof, InferredColumn, @@ -60,7 +63,10 @@ from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily from sqlbuild.sql_values.types import CollectionRendering -from tests.integration.src.sqlbuild.compiler.golden_views import GoldenEntry, golden_entry +from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + AdapterRuleCallbackTestCase, +) +from tests.integration.src.sqlbuild.compiler.helpers import GoldenEntry, golden_entry _PROJECT_TOML: str = ( 'name = "orders_analysis"\nadapter = "duckdb"\n\n[connection]\ndatabase = "orders.duckdb"\n' @@ -744,11 +750,15 @@ def _compare_proofs( column_nullability_by_table=request.column_nullability_by_table, dynamic_families_by_table=request.dynamic_families_by_table, ) - for model_input in request.model_inputs: + proved: list[bool] = [ + bool(model_dynamic_families(model_input)) + and model_input.model_file.file_path.stem in analyses + for model_input in request.model_inputs + ] + pivot_inputs: list[CompileModelInput] = list(itertools.compress(request.model_inputs, proved)) + for model_input in pivot_inputs: families: tuple[SchemaDynamicColumnFamily, ...] = model_dynamic_families(model_input) - analysis: ModelSqlAnalysis | None = analyses.get(model_input.model_file.file_path.stem) - if not families or analysis is None: - continue + analysis: ModelSqlAnalysis = analyses[model_input.model_file.file_path.stem] models: tuple[tuple[str, tuple[SchemaDynamicColumnFamily, ...]], ...] = ( ( model_pivot_sql( @@ -802,3 +812,94 @@ def _analysis_views(analyses: dict[str, ModelSqlAnalysis]) -> object: def _catalog_view(catalog: Any) -> tuple[object, ...]: return (dict(catalog.schemas), dict(catalog.analysis_shapes)) + + +class RecordingNullabilityRule: + """An adapter nullability rule recording its calls, answering nullable or raising.""" + + def __init__(self, *, raised: type[Exception] | None) -> None: + self.calls: list[tuple[InferredNullability, ...]] = [] + self._outcomes: list[Callable[[], InferredNullability]] = [ + _nullable, + *(functools.partial(_raise, error) for error in filter(None, (raised,))), + ] + + def __call__(self, arguments: tuple[InferredNullability, ...]) -> InferredNullability: + self.calls.append(arguments) + return self._outcomes[-1]() + + +def adapter_rule_request( + *, + test_case: AdapterRuleCallbackTestCase, + rule: RecordingNullabilityRule, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> NativeModelAnalysisRequest: + """A generated project plus the adapter-rule models, analysed with `rule` for UPPER and IFF.""" + + request: NativeModelAnalysisRequest = analysis_request( + inputs=compile_inputs( + project_dir=tmp_path / "project", + files={ + **generated_analysis_files( + rng=random.Random(test_case.seed), model_count=test_case.model_count + ), + **ADAPTER_RULE_MODELS, + }, + ), + monkeypatch=monkeypatch, + inference_profile=ExpressionInferenceProfile( + sql_analysis_dialect="duckdb", + function_nullability_rules={"UPPER": rule, "IFF": rule}, + ), + ) + rule.calls.clear() + return request + + +def _nullable() -> InferredNullability: + return InferredNullability.NULLABLE + + +def _raise(error: type[Exception]) -> InferredNullability: + raise error("adapter rule failed") + + +def write_shared_query_project( + *, project_dir: Path, models: dict[str, str], upstream_sql: str +) -> None: + """Write an orders project with `orders`, `customers` and `models`.""" + + _ = (project_dir / "sqlbuild_project.toml").write_text( + 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n', encoding="utf-8" + ) + model_dir: Path = project_dir / "models" + model_dir.mkdir() + for name, sql in {"orders": upstream_sql, "customers": upstream_sql, **models}.items(): + _ = (model_dir / f"{name}.sql").write_text(sql, encoding="utf-8") + + +def upstream_lineage_sources( + *, project_dir: Path, column: str, mode: str, capsys: pytest.CaptureFixture[str] +) -> tuple[int, list[str]]: + """The lineage command's exit code and the upstream resources of `column` one level up.""" + + code: int = main( + [ + "--project-dir", + str(project_dir), + "lineage", + column, + "--format", + "json", + "--direction", + "upstream", + "--depth", + "1", + "--mode", + mode, + ] + ) + trace: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["trace"] + return code, [step["source"]["resource_name"] for step in trace] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py index f1e03acc70..11ab086dac 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py @@ -15,7 +15,7 @@ from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( CteFactRecoveryParityTestCase, ) -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( golden_differences, golden_entry, golden_name, diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index fb278d29bc..6a1a488a99 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -2,6 +2,7 @@ from __future__ import annotations +import itertools import random from collections import Counter from itertools import product @@ -40,8 +41,9 @@ ADAPTER_RULE_MODELS, NativeAnalysisRuns, NativePivotProofs, + RecordingNullabilityRule, + adapter_rule_request, analyse_natively, - analysis_request, compile_inputs, compiled_project_view, custom_nullability_rule, @@ -53,12 +55,12 @@ shared_analysis_files, started_sessions, ) -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( golden_differences, golden_name, + mismatches, read_golden, ) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches _DIALECT_PROFILES: tuple[ExpressionInferenceProfile, ...] = tuple( ExpressionInferenceProfile(sql_analysis_dialect=dialect) @@ -507,6 +509,33 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with raised=None, expected_minimum_calls=1, ), + ], + ids=lambda case: case.description, +) +def test_given_adapter_nullability_rule_when_analysing_natively_then_rule_is_called_back( + test_case: AdapterRuleCallbackTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + rule: RecordingNullabilityRule = RecordingNullabilityRule(raised=test_case.raised) + request: NativeModelAnalysisRequest = adapter_rule_request( + test_case=test_case, rule=rule, tmp_path=tmp_path, monkeypatch=monkeypatch + ) + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + + assert analyze_native_model_sql(request=request).analyses + assert len(rule.calls) >= test_case.expected_minimum_calls, test_case.description + assert all( + isinstance(value, InferredNullability) + for value in itertools.chain.from_iterable(rule.calls) + ) + assert deferral_kinds(record_dir) == Counter( + {"analysis_session:adapter_nullability_callback": 1} + ), test_case.description + + +@pytest.mark.parametrize( + "test_case", + [ AdapterRuleCallbackTestCase( description="the adapter rule's exception reaches the caller unchanged", seed=20261012, @@ -517,47 +546,20 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with ], ids=lambda case: case.description, ) -def test_given_adapter_nullability_rule_when_analysing_natively_then_rule_is_called_back( +def test_given_failing_adapter_nullability_rule_when_analysing_natively_then_its_error_raises( test_case: AdapterRuleCallbackTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - calls: list[tuple[InferredNullability, ...]] = [] - raised: list[type[Exception]] = [] - - def adapter_rule(arguments: tuple[InferredNullability, ...]) -> InferredNullability: - calls.append(arguments) - if raised: - raise raised[0]("adapter rule failed") - return InferredNullability.NULLABLE - - request: NativeModelAnalysisRequest = analysis_request( - inputs=compile_inputs( - project_dir=tmp_path / "project", - files={ - **generated_analysis_files( - rng=random.Random(test_case.seed), model_count=test_case.model_count - ), - **ADAPTER_RULE_MODELS, - }, - ), - monkeypatch=monkeypatch, - inference_profile=ExpressionInferenceProfile( - sql_analysis_dialect="duckdb", - function_nullability_rules={"UPPER": adapter_rule, "IFF": adapter_rule}, - ), + rule: RecordingNullabilityRule = RecordingNullabilityRule(raised=test_case.raised) + request: NativeModelAnalysisRequest = adapter_rule_request( + test_case=test_case, rule=rule, tmp_path=tmp_path, monkeypatch=monkeypatch ) - calls.clear() - raised.extend(filter(None, (test_case.raised,))) record_dir: Path = tmp_path / "records" monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) - if test_case.raised is None: - assert analyze_native_model_sql(request=request).analyses - else: - with pytest.raises(test_case.raised, match="adapter rule failed"): - _ = analyze_native_model_sql(request=request) + with pytest.raises(LookupError, match="adapter rule failed"): + _ = analyze_native_model_sql(request=request) - assert len(calls) >= test_case.expected_minimum_calls, test_case.description - assert all(isinstance(value, InferredNullability) for args in calls for value in args) + assert len(rule.calls) >= test_case.expected_minimum_calls, test_case.description assert deferral_kinds(record_dir) == Counter( {"analysis_session:adapter_nullability_callback": 1} ), test_case.description diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py index 379392f8df..f97308bc66 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py @@ -58,14 +58,14 @@ def test_given_unprojectable_model_when_compiling_then_parsed_tree_columns_are_k ) model: CompiledModel = project.models[0] + assert model.inferred_columns is not None assert ( - None - if model.inferred_columns is None - else tuple( + tuple( (column.name, column.type, column.nullability.value) for column in model.inferred_columns ) - ) == test_case.expected_columns + == test_case.expected_columns + ) @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py index a2755c5192..ec666691b3 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_shared_query_outputs.py @@ -15,6 +15,10 @@ from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( SharedQueryOutputTestCase, ) +from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( + upstream_lineage_sources, + write_shared_query_project, +) _UPSTREAM_SQL: str = ( "MODEL (description 'Test model.', materialized view); SELECT 1 AS id, 2.5 AS amount" @@ -27,39 +31,6 @@ _MISSING: str = _HEADER + 'SELECT id FROM __ref("{upstream}") WHERE missing > 0' -def _write_project(*, project_dir: Path, models: dict[str, str]) -> None: - _ = (project_dir / "sqlbuild_project.toml").write_text( - 'name = "orders"\nadapter = "duckdb"\n[rules]\nselect = []\n', encoding="utf-8" - ) - model_dir: Path = project_dir / "models" - model_dir.mkdir() - for name, sql in {"orders": _UPSTREAM_SQL, "customers": _UPSTREAM_SQL, **models}.items(): - _ = (model_dir / f"{name}.sql").write_text(sql, encoding="utf-8") - - -def _upstream_sources( - *, project_dir: Path, column: str, mode: str, capsys: pytest.CaptureFixture[str] -) -> tuple[int, list[str]]: - code: int = main( - [ - "--project-dir", - str(project_dir), - "lineage", - column, - "--format", - "json", - "--direction", - "upstream", - "--depth", - "1", - "--mode", - mode, - ] - ) - trace: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["trace"] - return code, [step["source"]["resource_name"] for step in trace] - - @pytest.mark.parametrize( "test_case", [ @@ -142,12 +113,14 @@ def test_given_equal_shapes_when_compiling_then_each_model_keeps_its_own_lineage tmp_path: Path, capsys: pytest.CaptureFixture[str], ) -> None: - _write_project(project_dir=tmp_path, models=test_case.models) + write_shared_query_project( + project_dir=tmp_path, models=test_case.models, upstream_sql=_UPSTREAM_SQL + ) compiled: int = main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) diagnostics: list[object] = json.loads(capsys.readouterr().out)["diagnostics"] traces: list[tuple[int, list[str]]] = [ - _upstream_sources(project_dir=tmp_path, column=column, mode=mode, capsys=capsys) + upstream_lineage_sources(project_dir=tmp_path, column=column, mode=mode, capsys=capsys) for column, _, mode in test_case.expected_lineage ] @@ -188,7 +161,9 @@ def test_given_equal_shapes_with_findings_when_compiling_then_reports_each_model tmp_path: Path, capsys: pytest.CaptureFixture[str], ) -> None: - _write_project(project_dir=tmp_path, models=test_case.models) + write_shared_query_project( + project_dir=tmp_path, models=test_case.models, upstream_sql=_UPSTREAM_SQL + ) compiled: int = main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) diagnostics: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["diagnostics"] @@ -202,11 +177,6 @@ def test_given_equal_shapes_with_findings_when_compiling_then_reports_each_model ) for diagnostic in diagnostics ) - columns: dict[str, int] = { - model: sql.index("missing") + 1 - for model, sql in test_case.models.items() - if "missing" in sql - } assert compiled == 1 assert findings == sorted( @@ -214,8 +184,8 @@ def test_given_equal_shapes_with_findings_when_compiling_then_reports_each_model model, "B002", f"Unknown column 'missing' in {upstream}", - columns[model], - columns[model] + len("missing"), + test_case.models[model].index("missing") + 1, + test_case.models[model].index("missing") + 1 + len("missing"), ) for model, upstream in test_case.expected_findings ) diff --git a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py index 23aa659844..3822197d2b 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py +++ b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py @@ -30,7 +30,7 @@ render_error, write_project, ) -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, diff --git a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py index 1d15dee486..2e21d0a4f0 100644 --- a/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py +++ b/tests/integration/src/sqlbuild/compiler/contracts/test_native_model_contracts.py @@ -14,6 +14,7 @@ UnknownDialectContractTestCase, ) from tests.integration.src.sqlbuild.compiler.contracts.helpers import ( + ContractView, NativeContractRecord, compiled_contract_project, contract_diagnostics, @@ -22,7 +23,7 @@ record_native_outcomes, with_declared_type, ) -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, @@ -168,7 +169,7 @@ def test_given_input_python_once_answered_when_validating_then_native_diagnostic declared_type=test_case.declared_type, ) - diagnostics = contract_diagnostics(project=project, dialect=test_case.dialect) + diagnostics: ContractView = contract_diagnostics(project=project, dialect=test_case.dialect) assert ( tuple( diff --git a/tests/integration/src/sqlbuild/compiler/golden_views.py b/tests/integration/src/sqlbuild/compiler/golden_views.py deleted file mode 100644 index e5596119d4..0000000000 --- a/tests/integration/src/sqlbuild/compiler/golden_views.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Expected outputs recorded once from the pre-port Python implementations, compared by digest. - -Each golden file holds the Python oracle's view of every seam call a test makes, in call order, -as `[kind, digest]` or `[kind, {name: digest}]` entries. A test rebuilds the same entries from -the native result and must match them exactly, so two equal native runs cannot stand in for -the deleted Python oracle. -""" - -from __future__ import annotations - -import dataclasses -import hashlib -import json -import re -from enum import Enum -from pathlib import Path -from typing import cast - -GOLDEN_DIR: Path = Path(__file__).resolve().parents[4] / "goldens" / "python_oracle" - -type GoldenEntry = list[object] - - -def canonical(value: object) -> object: - """A JSON value standing for `value`, equal exactly when the Python values are equal.""" - - if isinstance(value, Enum): - return [type(value).__name__, canonical(value.value)] - if dataclasses.is_dataclass(value) and not isinstance(value, type): - return [ - type(value).__name__, - { - field.name: canonical(getattr(value, field.name)) - for field in dataclasses.fields(value) - }, - ] - if isinstance(value, dict): - return [ - ["key", canonical(key), canonical(item)] - for key, item in sorted( - value.items(), key=lambda pair: json.dumps(canonical(pair[0]), sort_keys=True) - ) - ] - if isinstance(value, list | tuple): - return [type(value).__name__, [canonical(item) for item in value]] - if isinstance(value, frozenset | set): - return ["set", sorted((canonical(item) for item in value), key=json.dumps)] - if value is None or isinstance(value, bool | int | float | str): - return value - return ["repr", repr(value)] - - -def digest(value: object, *, masked: tuple[str, ...] = ()) -> str: - """The SHA-256 of `value`'s canonical JSON, with `masked` texts such as temporary paths - replaced, shortened for golden files.""" - - text: str = json.dumps(canonical(value), sort_keys=True, ensure_ascii=False) - for mask in masked: - text = text.replace(mask, "") - return hashlib.sha256(text.encode()).hexdigest()[:24] - - -def golden_entry(kind: str, view: object, *, masked: tuple[str, ...] = ()) -> GoldenEntry: - """One seam call's golden entry: a digest per named item for mappings, else one digest.""" - - if isinstance(view, dict): - return [kind, {str(name): digest(item, masked=masked) for name, item in view.items()}] - return [kind, digest(view, masked=masked)] - - -def golden_name(prefix: str, description: str) -> str: - """The golden file name of a test case: its prefix and its description as a slug.""" - - return f"{prefix}_{re.sub(r'[^a-z0-9]+', '_', description.lower()).strip('_')}" - - -def read_golden(name: str) -> list[GoldenEntry]: - """The recorded entries of one golden file.""" - - return json.loads((GOLDEN_DIR / f"{name}.json").read_text(encoding="utf-8")) - - -def write_golden(name: str, entries: list[GoldenEntry]) -> None: - """Record one golden file; used only by the one-off oracle recording.""" - - GOLDEN_DIR.mkdir(parents=True, exist_ok=True) - _ = (GOLDEN_DIR / f"{name}.json").write_text( - json.dumps(entries, indent=0, sort_keys=True) + "\n", encoding="utf-8" - ) - - -def golden_differences( - expected: list[GoldenEntry], actual: list[GoldenEntry] -) -> list[tuple[int, str, object, object]]: - """`(index, kind, expected, actual)` for every entry that differs, names compared one by one.""" - - differences: list[tuple[int, str, object, object]] = [] - for index in range(max(len(expected), len(actual))): - want: GoldenEntry | None = expected[index] if index < len(expected) else None - got: GoldenEntry | None = actual[index] if index < len(actual) else None - if want == got: - continue - want_items: object = want[1] if want is not None else None - got_items: object = got[1] if got is not None else None - if ( - want is not None - and got is not None - and want[0] == got[0] - and isinstance(want_items, dict) - and isinstance(got_items, dict) - ): - want_digests: dict[str, object] = cast(dict[str, object], want_items) - got_digests: dict[str, object] = cast(dict[str, object], got_items) - differences.extend( - (index, f"{want[0]}:{name}", want_digests.get(name), got_digests.get(name)) - for name in sorted({*want_digests, *got_digests}) - if want_digests.get(name) != got_digests.get(name) - ) - continue - differences.append( - (index, str((want or got or ["?"])[0]), want and want[1], got and got[1]) - ) - return differences diff --git a/tests/integration/src/sqlbuild/compiler/helpers.py b/tests/integration/src/sqlbuild/compiler/helpers.py index 1043358d25..4b6a6df9d4 100644 --- a/tests/integration/src/sqlbuild/compiler/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/helpers.py @@ -2,15 +2,22 @@ from __future__ import annotations +import dataclasses +import functools +import hashlib +import itertools import json import math import random +import re import struct from collections.abc import Callable from contextlib import suppress +from enum import Enum from itertools import compress from operator import ne from pathlib import Path +from typing import Any, cast import orjson @@ -337,3 +344,185 @@ def mutated_word(*, rng: random.Random, candidates: list[str]) -> str: for _ in range(rng.randint(0, 4)): rng.choice(MUTATIONS)(rng=rng, word=word, position=rng.randint(0, len(word))) return "".join(word) + + +GOLDEN_DIR: Path = Path(__file__).resolve().parents[4] / "goldens" / "python_oracle" + +type GoldenEntry = list[object] +type GoldenDifference = tuple[int, str, object, object] + + +def canonical(value: object) -> object: + """A JSON value standing for `value`, equal exactly when the Python values are equal.""" + + return _CANONICAL_VIEWS[_canonical_kind(value)](value) + + +def _canonical_kind(value: object) -> str: + kinds: tuple[tuple[str, bool], ...] = ( + ("enum", isinstance(value, Enum)), + ("dataclass", dataclasses.is_dataclass(value) and not isinstance(value, type)), + ("dict", isinstance(value, dict)), + ("sequence", isinstance(value, list | tuple)), + ("set", isinstance(value, frozenset | set)), + ("scalar", value is None or isinstance(value, bool | int | float | str)), + ("repr", True), + ) + return _first_matching(kinds) + + +def _canonical_enum(value: object) -> object: + return [type(value).__name__, canonical(cast(Enum, value).value)] + + +def _canonical_dataclass(value: object) -> object: + fields: tuple[dataclasses.Field[object], ...] = dataclasses.fields(cast(Any, value)) + return [ + type(value).__name__, + {field.name: canonical(getattr(value, field.name)) for field in fields}, + ] + + +def _canonical_dict(value: object) -> object: + items: list[tuple[object, object]] = sorted( + cast(dict[object, object], value).items(), + key=lambda pair: json.dumps(canonical(pair[0]), sort_keys=True), + ) + return [["key", canonical(key), canonical(item)] for key, item in items] + + +def _canonical_sequence(value: object) -> object: + return [type(value).__name__, [canonical(item) for item in cast(tuple[object, ...], value)]] + + +def _canonical_set(value: object) -> object: + return ["set", sorted((canonical(item) for item in cast(set[object], value)), key=json.dumps)] + + +_CANONICAL_VIEWS: dict[str, Callable[[object], object]] = { + "enum": _canonical_enum, + "dataclass": _canonical_dataclass, + "dict": _canonical_dict, + "sequence": _canonical_sequence, + "set": _canonical_set, + "scalar": lambda value: value, + "repr": lambda value: ["repr", repr(value)], +} + + +def digest(value: object, *, masked: tuple[str, ...] = ()) -> str: + """The short SHA-256 of `value`'s canonical JSON, with `masked` texts replaced.""" + + text: str = functools.reduce( + lambda current, mask: current.replace(mask, ""), + masked, + json.dumps(canonical(value), sort_keys=True, ensure_ascii=False), + ) + return hashlib.sha256(text.encode()).hexdigest()[:24] + + +def golden_entry(kind: str, view: object, *, masked: tuple[str, ...] = ()) -> GoldenEntry: + """One seam call's golden entry: a digest per named item for mappings, else one digest.""" + + entries: tuple[Callable[[], GoldenEntry], ...] = ( + lambda: [kind, digest(view, masked=masked)], + lambda: [ + kind, + { + str(name): digest(item, masked=masked) + for name, item in cast(dict[object, object], view).items() + }, + ], + ) + return entries[isinstance(view, dict)]() + + +def golden_name(prefix: str, description: str) -> str: + """The golden file name of a test case: its prefix and its description as a slug.""" + + return f"{prefix}_{re.sub(r'[^a-z0-9]+', '_', description.lower()).strip('_')}" + + +def read_golden(name: str) -> list[GoldenEntry]: + """The entries recorded once from the pre-port Python implementation, in call order.""" + + return json.loads((GOLDEN_DIR / f"{name}.json").read_text(encoding="utf-8")) + + +def write_golden(name: str, entries: list[GoldenEntry]) -> None: + """Record one golden file; used only by the one-off oracle recording.""" + + GOLDEN_DIR.mkdir(parents=True, exist_ok=True) + _ = (GOLDEN_DIR / f"{name}.json").write_text( + json.dumps(entries, indent=0, sort_keys=True) + "\n", encoding="utf-8" + ) + + +def golden_differences( + expected: list[GoldenEntry], actual: list[GoldenEntry] +) -> list[GoldenDifference]: + """`(index, kind, expected, actual)` for every entry that differs, names compared one by one.""" + + pairs: list[tuple[GoldenEntry | None, GoldenEntry | None]] = list( + itertools.zip_longest(expected, actual) + ) + return list( + itertools.chain.from_iterable( + _entry_differences(index, want, got) for index, (want, got) in enumerate(pairs) + ) + ) + + +def _entry_differences( + index: int, want: GoldenEntry | None, got: GoldenEntry | None +) -> list[GoldenDifference]: + kinds: tuple[tuple[str, bool], ...] = ( + ("equal", want == got), + ("named", _named_entries(want, got)), + ("whole", True), + ) + kind: str = _first_matching(kinds) + return _ENTRY_DIFFERENCES[kind](index, want, got) + + +def _named_entries(want: GoldenEntry | None, got: GoldenEntry | None) -> bool: + return ( + want is not None + and got is not None + and want[0] == got[0] + and isinstance(want[1], dict) + and isinstance(got[1], dict) + ) + + +def _named_differences( + index: int, want: GoldenEntry | None, got: GoldenEntry | None +) -> list[GoldenDifference]: + kind: object = cast(GoldenEntry, want)[0] + want_digests: dict[str, object] = cast(dict[str, object], cast(GoldenEntry, want)[1]) + got_digests: dict[str, object] = cast(dict[str, object], cast(GoldenEntry, got)[1]) + names: list[str] = sorted({*want_digests, *got_digests}) + differing: list[bool] = [want_digests.get(name) != got_digests.get(name) for name in names] + return [ + (index, f"{kind}:{name}", want_digests.get(name), got_digests.get(name)) + for name in itertools.compress(names, differing) + ] + + +def _whole_difference( + index: int, want: GoldenEntry | None, got: GoldenEntry | None +) -> list[GoldenDifference]: + return [(index, str((want or got or ["?"])[0]), want and want[1], got and got[1])] + + +_ENTRY_DIFFERENCES: dict[ + str, Callable[[int, GoldenEntry | None, GoldenEntry | None], list[GoldenDifference]] +] = { + "equal": lambda _index, _want, _got: [], + "named": _named_differences, + "whole": _whole_difference, +} + + +def _first_matching(kinds: tuple[tuple[str, bool], ...]) -> str: + return next(itertools.compress([kind for kind, _ in kinds], [matches for _, matches in kinds])) diff --git a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py index 2cf9726902..fd01d583e1 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py @@ -17,14 +17,14 @@ ) from sqlbuild.compiler.lineage.models import ProjectColumnLineage from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, golden_name, + mismatches, read_golden, ) -from tests.integration.src.sqlbuild.compiler.helpers import mismatches from tests.integration.src.sqlbuild.compiler.lineage._test_types import ( FormerlyDeferredLineageTestCase, GeneratedLineageParityTestCase, @@ -181,6 +181,36 @@ def test_given_unparsable_model_when_building_fast_lineage_then_logs_and_omits_i expected_native_statuses={"built": _MODEL_COUNT}, expected_error=None, ), + ], + ids=lambda case: case.description, +) +def test_given_formerly_deferred_models_when_building_fast_lineage_then_native_answers( + test_case: FormerlyDeferredLineageTestCase, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + statuses: Counter[str] = record_native_outcomes(monkeypatch=monkeypatch) + compiled: CompiledProject = without_compact_facts( + compiled_project( + project_dir=tmp_path / "project", + files=generated_lineage_files(rng=random.Random(7), model_count=_MODEL_COUNT), + ) + ) + project: CompiledProject = replace( + compiled, binding_catalog=(None, compiled.binding_catalog)[test_case.keeps_catalog] + ) + + _ = lineage_views(project=project, dialect=test_case.dialect, model_names=None) + + assert statuses == Counter(test_case.expected_native_statuses), test_case.description + assert deferral_records(record_dir) == [] + + +@pytest.mark.parametrize( + "test_case", + [ FormerlyDeferredLineageTestCase( description="a dialect name Polyglot does not know raises Python's error", dialect="nonsense", @@ -191,7 +221,7 @@ def test_given_unparsable_model_when_building_fast_lineage_then_logs_and_omits_i ], ids=lambda case: case.description, ) -def test_given_formerly_deferred_models_when_building_fast_lineage_then_native_answers( +def test_given_unknown_dialect_when_building_fast_lineage_then_python_error_raises( test_case: FormerlyDeferredLineageTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, @@ -209,11 +239,8 @@ def test_given_formerly_deferred_models_when_building_fast_lineage_then_native_a compiled, binding_catalog=(None, compiled.binding_catalog)[test_case.keeps_catalog] ) - if test_case.expected_error is None: + with pytest.raises(ValueError, match=test_case.expected_error): _ = lineage_views(project=project, dialect=test_case.dialect, model_names=None) - else: - with pytest.raises(ValueError, match=test_case.expected_error): - _ = lineage_views(project=project, dialect=test_case.dialect, model_names=None) assert statuses == Counter(test_case.expected_native_statuses), test_case.description assert deferral_records(record_dir) == [] diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py index 3d75e75b04..5dad53d70c 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/helpers.py @@ -5,6 +5,9 @@ from pathlib import Path from typing import Any +import pytest + +import sqlbuild._native as native_module from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs @@ -204,3 +207,17 @@ def apply_warehouse_setup_sql(*, project_dir: Path, statements: tuple[str, ...]) connection.execute(statement) finally: connection.close() + + +def traced_shape_batches(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, ...]]: + """Record the expressions of every native expression-source shape batch.""" + + original: Callable[..., Any] = native_module.infer_expression_source_shapes + batches: list[tuple[str, ...]] = [] + + def traced(catalog: object, request: tuple[Any, ...], *rules: Any) -> Any: + batches.append(tuple(request[4])) + return original(catalog, request, *rules) + + monkeypatch.setattr(native_module, "infer_expression_source_shapes", traced) + return batches diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py index e802ce57fd..6605544e64 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_expression_catalog_cache.py @@ -1,14 +1,12 @@ """Expression schemas are memoized only within their owning compiler catalog.""" import json -from collections.abc import Callable from dataclasses import replace from pathlib import Path from typing import Any import pytest -import sqlbuild._native as native_module from sqlbuild.cli.commands.main.entrypoint.entry import main from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import semantic_shapes from sqlbuild.compiler.compile.models import CompiledProject @@ -18,20 +16,7 @@ ExpressionMemoCase, RestoredShapeCase, ) - - -def traced_shape_batches(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, ...]]: - """Record the expressions of every native expression-source shape batch.""" - - original: Callable[..., Any] = native_module.infer_expression_source_shapes - batches: list[tuple[str, ...]] = [] - - def traced(catalog: object, request: tuple[Any, ...], *rules: Any) -> Any: - batches.append(tuple(request[4])) - return original(catalog, request, *rules) - - monkeypatch.setattr(native_module, "infer_expression_source_shapes", traced) - return batches +from tests.integration.src.sqlbuild.compiler.pipeline.helpers import traced_shape_batches @pytest.mark.parametrize( diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index 7db41eecf4..ffd4e5a78f 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -197,10 +197,8 @@ def test_given_projects_python_rejects_when_assembling_natively_then_raises_pyth assert test_case.expected_error in str(python_error.value) assert str(native_error.value) == str(python_error.value) - assert assembly_deferrals(record_dir) == ( - Counter({f"project_assembly:{test_case.expected_kind}": 1}) - if test_case.expected_kind is not None - else Counter() + assert assembly_deferrals(record_dir) == Counter( + f"project_assembly:{kind}" for kind in filter(None, (test_case.expected_kind,)) ) @@ -246,10 +244,8 @@ def test_given_a_variable_only_python_renders_when_assembling_natively_then_pyth assert assembly_view(native) == assembly_view(python) assert native.seeds[0].destination.schema == test_case.expected_schema - assert assembly_deferrals(record_dir) == ( - Counter({f"project_assembly:{test_case.expected_kind}": 1}) - if test_case.expected_kind is not None - else Counter() + assert assembly_deferrals(record_dir) == Counter( + {f"project_assembly:{test_case.expected_kind}": 1} ) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 1286925076..3807594f49 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -1,5 +1,6 @@ from __future__ import annotations +import functools import random import re from collections import Counter @@ -342,15 +343,38 @@ def non_ascii_variant(*, files: dict[str, str], rng: random.Random) -> dict[str, header holding non-ASCII text, digits and Python-only line separators.""" renamed: list[tuple[str, str]] = rng.sample(_NON_ASCII_NAMES, k=rng.randint(1, 6)) - variant: dict[str, str] = {} - for path, text in files.items(): - for old, new in renamed: - text = re.sub(rf"\b{old}\b", new, text) - if path.endswith(".sql") and ";" in text: - header_end: int = text.index(";") + 1 - text = text[:header_end] + rng.choice(_NON_ASCII_COMMENTS) + text[header_end:] - variant[path] = text - return variant + return { + path: _non_ascii_text(path=path, text=text, renamed=renamed, rng=rng) + for path, text in files.items() + } + + +def files_with_non_ascii( + *, files: dict[str, str], rng: random.Random, non_ascii: bool +) -> dict[str, str]: + """`files`, or with `non_ascii` their `non_ascii_variant`.""" + + variants: tuple[Callable[[], dict[str, str]], ...] = ( + lambda: files, + lambda: non_ascii_variant(files=files, rng=rng), + ) + return variants[non_ascii]() + + +def _non_ascii_text( + *, path: str, text: str, renamed: list[tuple[str, str]], rng: random.Random +) -> str: + renamed_text: str = functools.reduce( + lambda current, names: re.sub(rf"\b{names[0]}\b", names[1], current), renamed, text + ) + commented: bool = path.endswith(".sql") and ";" in renamed_text + comments: tuple[Callable[[], str], ...] = ( + lambda: "", + lambda: rng.choice(_NON_ASCII_COMMENTS), + ) + comment: str = comments[commented]() + header_end: int = renamed_text.find(";") + 1 + return renamed_text[:header_end] + comment + renamed_text[header_end:] def semantic_outcome(complete: Callable[[], object]) -> tuple[str, object]: @@ -366,7 +390,7 @@ def outcome_label(outcome: tuple[str, object]) -> str: """`completed`, or the error as `Type: message`.""" kind, value = outcome - return kind if kind == "completed" else f"{kind}: {value}" + return {"completed": kind}.get(kind, f"{kind}: {value}") _SOURCE_COLUMNS: tuple[str, ...] = ( diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py index c724cbd2bc..cd3675054c 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_metadata_checks.py @@ -9,7 +9,7 @@ import pytest -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, @@ -22,20 +22,15 @@ from tests.integration.src.sqlbuild.compiler.semantic_checks.helpers import ( SemanticInputs, captured_semantic_inputs, + files_with_non_ascii, generated_metadata_files, native_metadata, - non_ascii_variant, record_metadata_families, semantic_outcome, with_dialect, ) -def metadata_files(*, rng: random.Random, model_count: int, non_ascii: bool) -> dict[str, str]: - files: dict[str, str] = generated_metadata_files(rng=rng, model_count=model_count) - return non_ascii_variant(files=files, rng=rng) if non_ascii else files - - @pytest.mark.parametrize( "test_case", [ @@ -83,8 +78,10 @@ def test_given_generated_projects_when_checking_metadata_natively_then_matches_r project_dir: Path = tmp_path / f"project_{index}" captured: SemanticInputs = captured_semantic_inputs( project_dir=project_dir, - files=metadata_files( - rng=rng, model_count=test_case.model_count, non_ascii=test_case.non_ascii + files=files_with_non_ascii( + files=generated_metadata_files(rng=rng, model_count=test_case.model_count), + rng=rng, + non_ascii=test_case.non_ascii, ), monkeypatch=monkeypatch, ) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index 678d81990e..41e10deb77 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -11,7 +11,7 @@ import sqlbuild._native as native_module from sqlbuild.compiler.compile.models import CompiledProject -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_entry, @@ -29,9 +29,9 @@ captured_semantic_inputs, completed_natively, completion_view, + files_with_non_ascii, generated_semantic_files, native_completion, - non_ascii_variant, note_kinds, outcome_label, proven_output_count, @@ -162,7 +162,7 @@ def test_given_formerly_deferred_projects_when_completing_natively_then_matches_ ) captured: SemanticInputs = captured_semantic_inputs( project_dir=project_dir, - files=non_ascii_variant(files=files, rng=rng) if test_case.non_ascii else files, + files=files_with_non_ascii(files=files, rng=rng, non_ascii=test_case.non_ascii), monkeypatch=monkeypatch, ) views: dict[str, object] = {} diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index d26d004b90..8091acb997 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -29,7 +29,7 @@ from sqlbuild.compiler.sql_test_glue.types import ( NativeSqlTestAssemblyRow, ) -from tests.integration.src.sqlbuild.compiler.golden_views import GoldenEntry, golden_entry +from tests.integration.src.sqlbuild.compiler.helpers import GoldenEntry, golden_entry @dataclass(frozen=True) @@ -481,12 +481,14 @@ def record_native_assemblies(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str] def counted_failures(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: rows: list[NativeSqlTestAssemblyRow] = assemble(request) - answers.update(f"failed_{failure[0]}" for _, failure in rows if failure is not None) + failures: list[tuple[str, str] | None] = [failure for _, failure in rows] + answers.update(f"failed_{failure[0]}" for failure in filter(None, failures)) return rows def counted_assemblies(*, inputs: CompileProjectInputs) -> tuple[NativeSqlTestAssembly, ...]: assemblies: tuple[NativeSqlTestAssembly, ...] = seam(inputs=inputs) - native: list[CompiledSqlTest] = [item.test for item in assemblies if item.test is not None] + tests: list[CompiledSqlTest | None] = [item.test for item in assemblies] + native: list[CompiledSqlTest] = list(filter(None, tests)) answers["native_assembled"] += len(native) answers["native_with_diagnostics"] += sum(bool(item.diagnostics) for item in assemblies) answers["native_case_fingerprints"] += sum( @@ -503,7 +505,7 @@ def assembly_golden_entry(*, outcome: PlanningCallOutcome, project_dir: Path) -> """One project's compiled SQL tests, diagnostics and raised error as a golden entry.""" tests, diagnostics = cast( - tuple[object, object], outcome.value if outcome.raised is None else (None, None) + tuple[object, object], (outcome.value, (None, None))[outcome.raised is not None] ) return golden_entry( "sql_test_assembly", diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py index 444129c43b..a553eeefa3 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_assembly.py @@ -13,7 +13,7 @@ import pytest -from tests.integration.src.sqlbuild.compiler.golden_views import ( +from tests.integration.src.sqlbuild.compiler.helpers import ( GoldenEntry, golden_differences, golden_name, diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py index 06adc0c849..d87ed36445 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/helpers.py @@ -1,3 +1,4 @@ +import itertools from collections.abc import Callable from dataclasses import dataclass, replace from pathlib import Path @@ -5,11 +6,16 @@ from typing import cast from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.bigquery.classes.bigquery_adapter import BigQueryAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.adapters.postgres.classes.postgres_adapter import PostgresAdapter from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter from sqlbuild.adapters.sqlserver.classes.sqlserver_adapter import SqlServerAdapter +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, +) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns from sqlbuild.compiler.compile._helpers.attachment import core as attachment_core from sqlbuild.compiler.compile._helpers.diagnostics.collector import ( collect_compile_diagnostics, @@ -68,7 +74,11 @@ DiscoveredTaskFunction, ) from sqlbuild.compiler.graph.main._build_lineage_upstream_deps import build_lineage_upstream_deps -from sqlbuild.compiler.lineage.types import ColumnLineageConfidence, ColumnTransformKind +from sqlbuild.compiler.lineage.types import ( + ColumnLineageConfidence, + ColumnTransformKind, + InferredNullability, +) from sqlbuild.compiler.model_loop.main._build_native_declaration_contexts import ( build_native_declaration_contexts, ) @@ -812,3 +822,29 @@ def extract_test_sql( return extract_unexpanded_sql_test( sql=sql, file_label=file_label, mode=mode, syntax=SqlLexicalSyntax() )[0] + + +def reanalysed_columns( + *, + query_sql: str, + references: tuple[CompileSqlReference, ...] = (), + column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, + column_types_by_table: dict[str, dict[str, str]] | None = None, + inference_profile: ExpressionInferenceProfile | None = None, + allow_compact_analysis: bool = True, + recover_cte_facts: bool = True, +) -> NativeQueryColumns: + """Native compact re-analysis over known input types, as source bindings run it.""" + + assert allow_compact_analysis and recover_cte_facts + values: list[InferredNullability] = list( + itertools.chain.from_iterable( + map(dict.values, (column_nullability_by_table or {}).values()) + ) + ) + assert all(value == InferredNullability.UNKNOWN for value in values) + return infer_native_query_columns( + queries=(NativeColumnQuery(sql=query_sql, mode="reanalysis", references=references),), + profile=inference_profile or ExpressionInferenceProfile(), + column_types_by_table=column_types_by_table, + )[0] diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py index e00468800a..4fbc721fb7 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py @@ -28,32 +28,10 @@ PolyglotAnalysisTestCase, SubstitutePlaceholderDefaultsTestCase, ) -from tests.unit.src.sqlbuild.compiler.compile._helpers.helpers import direct_orders_lineage - - -def _reanalysed( - *, - query_sql: str, - references: tuple[CompileSqlReference, ...] = (), - column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, - column_types_by_table: dict[str, dict[str, str]] | None = None, - inference_profile: ExpressionInferenceProfile | None = None, - allow_compact_analysis: bool = True, - recover_cte_facts: bool = True, -) -> NativeQueryColumns: - """Native compact re-analysis over known input types, as source bindings run it.""" - - assert allow_compact_analysis and recover_cte_facts - assert all( - value == InferredNullability.UNKNOWN - for shape in (column_nullability_by_table or {}).values() - for value in shape.values() - ) - return infer_native_query_columns( - queries=(NativeColumnQuery(sql=query_sql, mode="reanalysis", references=references),), - profile=inference_profile or ExpressionInferenceProfile(), - column_types_by_table=column_types_by_table, - )[0] +from tests.unit.src.sqlbuild.compiler.compile._helpers.helpers import ( + direct_orders_lineage, + reanalysed_columns, +) @pytest.mark.parametrize( @@ -611,7 +589,7 @@ def test_given_query_sql_when_inferring_columns_then_returns_expected( def test_given_ref_query_when_analyzing_columns_and_lineage_then_returns_compact_facts( test_case: PolyglotAnalysisTestCase, ) -> None: - result: NativeQueryColumns = _reanalysed( + result: NativeQueryColumns = reanalysed_columns( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, @@ -825,7 +803,7 @@ def test_given_ref_query_when_analyzing_columns_and_lineage_then_returns_compact def test_given_cte_chain_when_analyzing_direct_passthrough_then_type_is_conservative( test_case: PolyglotAnalysisTestCase, ) -> None: - result: NativeQueryColumns = _reanalysed( + result: NativeQueryColumns = reanalysed_columns( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, @@ -903,7 +881,7 @@ def test_given_cte_chain_when_analyzing_direct_passthrough_then_type_is_conserva def test_given_star_projection_when_compact_analysis_enabled_then_expands_schema_lineage( test_case: PolyglotAnalysisTestCase, ) -> None: - result: NativeQueryColumns = _reanalysed( + result: NativeQueryColumns = reanalysed_columns( query_sql=test_case.query_sql, references=test_case.references, column_nullability_by_table=test_case.column_nullability_by_table, From 7f21fa68bbd3a2c5f754d14ee9a979da39ed41b6 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:39:25 +0100 Subject: [PATCH 21/36] feat(native): expose each model's retained session columns and dynamic proof FinishedSession::model_analysis returns the typed columns compilation publishes for a model the session decides (a proven pivot's fixed columns, else a successful analysis's) and the session's dynamic proof, borrowed from the finished session, for the native compiled-project owner to record without re-analysis. --- .../src/assembly/analysis_session/models.rs | 30 ++++++++++++++++- .../analysis_session/tests/helpers.rs | 33 ++++++++++++------- .../tests/test_analysis_session.rs | 28 +++++++++++----- .../analysis_session/tests/test_types.rs | 10 ++++++ 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 3c48dab595..fb54ef0242 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -98,7 +98,35 @@ pub struct FinishedSession { pub(crate) pool: Result, String>, pub(crate) models: HashMap, /// Each model's dynamic pivot proof from the session's own relation facts, by model name. - pub dynamic_proofs: HashMap, + pub(crate) dynamic_proofs: HashMap, +} + +/// The session's typed result for one compiled model, borrowed from the finished session. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SessionModelAnalysis<'a> { + /// A proven pivot's fixed columns, else a successful analysis's; None where later decided. + pub columns: Option<&'a [ColumnFact]>, + /// The session's dynamic pivot proof, for a model declaring dynamic families. + pub dynamic_proof: Option<&'a ContractProof>, +} + +impl FinishedSession { + /// The session's typed columns and dynamic proof for model `name`, without re-analysis. + #[must_use] + pub fn model_analysis(&self, name: &str) -> SessionModelAnalysis<'_> { + let dynamic_proof: Option<&ContractProof> = self.dynamic_proofs.get(name); + let proven: Option<&[ColumnFact]> = dynamic_proof + .filter(|proof| proof.output_proven) + .map(|proof| proof.fixed_columns.as_slice()); + let analysed: Option<&[ColumnFact]> = self + .models + .get(name) + .and_then(|facts| facts.columns.as_deref()); + SessionModelAnalysis { + columns: proven.or(analysed), + dynamic_proof, + } + } } /// A compiled model's typed output columns and lineage from a native session analysis. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 511ad6562d..9e5b1a0127 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -18,7 +18,8 @@ use crate::assembly::analysis_session::main::take_analysis_cache::take_analysis_ use crate::assembly::analysis_session::models::{ AnalysisCacheStats, AnalysisSession, ColumnFact, ContractProof, DynamicFamily, FinishedSession, LineageRow, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, - PivotOutcome, PivotTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, + PivotOutcome, PivotTables, SessionModelAnalysis, SessionModelFacts, SessionOutcome, + SessionRequest, SessionStep, }; use crate::assembly::analysis_session::tests::test_types::{CachedRun, ModelSpec, RecoveredFacts}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -283,12 +284,12 @@ fn fact_line(facts: &SessionModelFacts) -> String { format!("{columns:?} {}", lineage.join(" ")) } -/// The proofs a finished session keeps for `pivot_request`'s models, analysed in one session. -pub(crate) fn kept_dynamic_proofs( +/// The proof and published columns a finished session keeps for `pivot_request`'s models. +pub(crate) fn kept_model_analyses( dialect: &str, sql: &str, family: Option<(&str, &str, &str)>, -) -> Vec { +) -> Vec<(PivotOutcome, Option>)> { let pivots: PivotBatchRequest = pivot_request(dialect, sql, family); let names: [&str; 2] = ["customer_amounts", "plain"]; let models: Vec = names @@ -313,16 +314,21 @@ pub(crate) fn kept_dynamic_proofs( let (_, _, finished) = completed(started(request)); names .iter() - .map(|name| { - finished - .dynamic_proofs - .get(*name) - .cloned() - .map_or(PivotOutcome::Absent, PivotOutcome::Proof) - }) + .map(|name| kept_line(finished.model_analysis(name))) .collect() } +fn kept_line(analysis: SessionModelAnalysis<'_>) -> (PivotOutcome, Option>) { + let proof: PivotOutcome = analysis + .dynamic_proof + .cloned() + .map_or(PivotOutcome::Absent, PivotOutcome::Proof); + let columns: Option> = analysis + .columns + .map(|columns| columns.iter().map(column_line).collect()); + (proof, columns) +} + /// Proofs over typed `raw_orders` and pivot `status_amounts`: the model, then a family-less one. pub(crate) fn pivot_request( dialect: &str, @@ -604,3 +610,8 @@ pub(crate) fn current_model_key(session: &AnalysisSession, name: &str) -> Conten .collect(); session.model_key(&ContentDigest::default(), model, &Vec::new(), &relations) } + +/// Owned copies of expected lines. +pub(crate) fn lines(expected: &[&str]) -> Vec { + expected.iter().map(|line| (*line).to_owned()).collect() +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index a680116d55..03e6f7d0fc 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -4,13 +4,13 @@ use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_ use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ - catalog, expected_facts, fact_lines, failed, finished_session, kept_dynamic_proofs, - legacy_lines, model_requests, orders_request, pivot_request, proven, recovered_facts, + catalog, expected_facts, fact_lines, failed, finished_session, kept_model_analyses, + legacy_lines, lines, model_requests, orders_request, pivot_request, proven, recovered_facts, session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ - CteRecoveryTestCase, LegacyAnalysisTestCase, ModelSpec, PivotTestCase, SessionFactsTestCase, - SessionFailureTestCase, SessionTestCase, UnscheduledTestCase, + CteRecoveryTestCase, KeptAnalysisTestCase, LegacyAnalysisTestCase, ModelSpec, PivotTestCase, + SessionFactsTestCase, SessionFailureTestCase, SessionTestCase, UnscheduledTestCase, }; /// A model whose missing column stays untyped over a known input, so completion re-analyses it. @@ -642,9 +642,9 @@ fn given_handed_back_models_when_analysing_legacy_then_matches_python_or_defers( } #[test] -fn given_a_finished_session_when_reading_dynamic_proofs_then_keeps_each_models_proof() { - let test_cases = [PivotTestCase { - description: "a bare DuckDB pivot keeps its proof; a family-less model keeps none", +fn given_a_finished_session_when_reading_model_analyses_then_keeps_typed_columns_and_proofs() { + let test_cases = [KeptAnalysisTestCase { + description: "a proven pivot publishes its fixed columns; a plain model its analysis", dialect: "duckdb", sql: "PIVOT __source(\"raw_orders\") ON status USING MAX(amount) GROUP BY customer_id", family: Some(("status", "amount", "MAX")), @@ -654,13 +654,23 @@ fn given_a_finished_session_when_reading_dynamic_proofs_then_keeps_each_models_p "raw_orders", true, ), + expected_columns: [&["customer_id INTEGER non_null"], &["1 - non_null"]], }]; for test_case in test_cases { - let kept = kept_dynamic_proofs(test_case.dialect, test_case.sql, test_case.family); + let kept = kept_model_analyses(test_case.dialect, test_case.sql, test_case.family); assert_eq!( kept, - vec![test_case.expected_outcome, PivotOutcome::Absent], + vec![ + ( + test_case.expected_outcome, + Some(lines(test_case.expected_columns[0])) + ), + ( + PivotOutcome::Absent, + Some(lines(test_case.expected_columns[1])) + ), + ], "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs index 41521e1cdc..8eeca03aa4 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs @@ -142,3 +142,13 @@ pub(crate) struct CachedRun { Vec, ), } + +pub(crate) struct KeptAnalysisTestCase { + pub(crate) description: &'static str, + pub(crate) dialect: &'static str, + pub(crate) sql: &'static str, + pub(crate) family: Option<(&'static str, &'static str, &'static str)>, + pub(crate) expected_outcome: PivotOutcome, + /// `name type nullability` of each model's published columns: the pivot, then a plain one. + pub(crate) expected_columns: [&'static [&'static str]; 2], +} From 7af82afe9d531ac5fa855bb03c7752ad40085a64 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:47:40 +0100 Subject: [PATCH 22/36] feat(native)!: scan nested macro arguments natively Each call's arguments are scanned by the native call-site scanner, which now reports where a call's arguments start and the name of a call Python's evaluation fails in, so every call, top level or nested, runs from native bounds. A failing call raises its macro lookup error first, then the scan's message, as Python's evaluation did. Python's macro call scanner, name parser and parenthesis matching are deleted. --- .../_helpers/render/macro_call_rows.rs | 5 +- .../bindings/_helpers/render/macro_calls.rs | 12 +- .../src/macro_calls/_helpers/scanner.rs | 5 + .../macro_calls/main/scan_macro_call_sites.rs | 27 +-- .../sqlbuild-render/src/macro_calls/models.rs | 4 + .../tests/test_scan_macro_call_sites.rs | 56 ++++++- .../src/macro_calls/tests/test_types.rs | 7 + src/sqlbuild/_native.pyi | 5 +- .../_helpers/macro_bridge/expansion.py | 98 ++++------- .../compile/_helpers/render/macros.py | 155 +++++++----------- .../macro_bridge/main/_scan_macro_calls.py | 9 +- src/sqlbuild/compiler/macro_bridge/models.py | 2 + ..._expansion_nested_argument_scan_edges.json | 74 +++++++++ .../sqlbuild/compiler/compile/_test_types.py | 9 + .../compile/test_native_macro_bridge.py | 52 ++++++ 15 files changed, 336 insertions(+), 184 deletions(-) create mode 100644 tests/goldens/python_oracle/macro_expansion_nested_argument_scan_edges.json diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs index 4dc2e378c8..bff5a278ed 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_call_rows.rs @@ -10,8 +10,8 @@ const DECLARATION_READ: u8 = 1; const GENERATED_SQL: u8 = 2; const ARGUMENT_REFERENCE: u8 = 3; -/// One call site: start, end, name, tree names if scanned and whether typed reference text appears. -pub(crate) type SiteRow = (usize, usize, String, Option>, bool); +/// One call site: start, arguments start, end, name, tree names if scanned and whether typed reference text appears. +pub(crate) type SiteRow = (usize, usize, usize, String, Option>, bool); /// One substitution span: source start and end, output start and end. pub(crate) type SpanRow = (usize, usize, usize, usize); /// One recorded event: its tag and two text fields. @@ -22,6 +22,7 @@ pub(crate) type EntryRow = (String, Vec<(String, String)>, Vec); pub(crate) fn site_row(site: MacroCallSite) -> SiteRow { ( site.start, + site.args_start, site.end, site.name, site.tree_names, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs index 282fdb56f2..fe4bc7b33d 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/render/macro_calls.rs @@ -18,8 +18,8 @@ use crate::bindings::types::CompilerDetach; const MACRO_SCAN_CONTEXT: &str = "native macro call scan"; -/// `(call start or None, message)` of the error Python's scan raises. -type FailureRow = (Option, &'static str); +/// `(call start or None, call name or None, message)` of the error Python's scan raises. +type FailureRow = (Option, Option, &'static str); /// Every complete top-level macro call site of `sql`, and where Python's scan raises, if it does. #[pyfunction] @@ -40,7 +40,13 @@ pub(crate) fn scan_macro_call_sites( .map_err(compiler_error)?; Ok(( sites.into_iter().map(site_row).collect(), - failure.map(|MacroScanFailure { call_start, error }| (call_start, error.message())), + failure.map( + |MacroScanFailure { + call_start, + call_name, + error, + }| (call_start, call_name, error.message()), + ), )) } diff --git a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs index c923751ae4..249c35e149 100644 --- a/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs +++ b/crates/sqlbuild-render/src/macro_calls/_helpers/scanner.rs @@ -59,6 +59,11 @@ pub(crate) fn top_level_calls( (calls, None) } +/// Python's `_parse_macro_name`: the identifier after the `@` at byte `start`. +pub(crate) fn call_name(python: PythonText, text: &str, start: usize) -> String { + text[start + 1..identifier_end(python, text, start + 1)].to_owned() +} + /// Python's `_scan_sqlbuild_call_names(include_nested=True)`: every call start's name in order. pub(crate) fn every_call_name(python: PythonText, text: &str) -> Result, ScanError> { let mut names: Vec = Vec::new(); diff --git a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs index a1b327e59d..ae7000146a 100644 --- a/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/main/scan_macro_call_sites.rs @@ -4,7 +4,7 @@ use sqlbuild_core::text::models::PythonText; use crate::macro_calls::_helpers::offsets::char_offsets; use crate::macro_calls::_helpers::scanner::{ - ScannedFailure, mentions_typed_reference, nested_names, top_level_calls, + call_name, mentions_typed_reference, nested_names, top_level_calls, }; use crate::macro_calls::models::{MacroCallScan, MacroCallSite, MacroScanFailure}; @@ -13,33 +13,38 @@ pub fn scan_macro_call_sites(python: PythonText, sql: &str) -> MacroCallScan { let (calls, failure) = top_level_calls(python, sql); let mut boundaries: Vec = calls .iter() - .flat_map(|call| [call.start, call.close + 1]) + .flat_map(|call| [call.start, call.open + 1, call.close + 1]) .collect(); let failed_call: Option = failure.as_ref().and_then(|failure| failure.call_start); boundaries.extend(failed_call); let offsets = char_offsets(sql, &boundaries); let sites: Vec = calls .into_iter() - .zip(offsets.as_chunks::<2>().0) + .zip(offsets.as_chunks::<3>().0) .map(|(call, offsets)| { let args = &sql[call.open + 1..call.close]; let tree_names: Option> = call_tree_names(python, &call.name, args); MacroCallSite { start: offsets[0], - end: offsets[1], + args_start: offsets[1], + end: offsets[2], name: call.name, tree_names, typed_reference_text: mentions_typed_reference(args), } }) .collect(); - MacroCallScan { - sites, - failure: failure.map(|ScannedFailure { call_start, error }| MacroScanFailure { - call_start: call_start.and(offsets.last().copied()), - error, - }), - } + let failure: Option = failure.map(|failure| MacroScanFailure { + call_start: failure.call_start.and(offsets.last().copied()), + call_name: failed_call_name(python, sql, failure.call_start), + error: failure.error, + }); + MacroCallScan { sites, failure } +} + +/// The name of the call at byte `start` that Python's evaluation raises in, if any. +fn failed_call_name(python: PythonText, sql: &str, start: Option) -> Option { + Some(call_name(python, sql, start?)) } /// The call's name, then each nested call's once; None where a nested scan raises. diff --git a/crates/sqlbuild-render/src/macro_calls/models.rs b/crates/sqlbuild-render/src/macro_calls/models.rs index 22f0ee1c93..37c2ba728f 100644 --- a/crates/sqlbuild-render/src/macro_calls/models.rs +++ b/crates/sqlbuild-render/src/macro_calls/models.rs @@ -16,6 +16,8 @@ use crate::macro_calls::_helpers::entry_codec::{decode_entry, encode_entry}; pub struct MacroCallSite { /// Offset of the `@` that starts the call. pub start: usize, + /// Offset just past the call's opening parenthesis, where its arguments start. + pub args_start: usize, /// Offset just past the call's closing parenthesis. pub end: usize, /// The called macro's name. @@ -31,6 +33,8 @@ pub struct MacroCallSite { pub struct MacroScanFailure { /// Offset of the `@` of the call Python's evaluation raises in, if any. pub call_start: Option, + /// The name of the call Python's evaluation raises in, if any. + pub call_name: Option, pub error: ScanError, } diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs index 7199def831..3f83a4f069 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_scan_macro_call_sites.rs @@ -4,7 +4,7 @@ use crate::macro_calls::tests::helpers::python_312; use std::time::Instant; use crate::macro_calls::tests::test_types::{ - DeepNestingTestCase, ExpectedSite, ScanMacroCallSitesTestCase, + CallBoundsTestCase, DeepNestingTestCase, ExpectedSite, ScanMacroCallSitesTestCase, }; #[test] @@ -235,3 +235,57 @@ fn given_deeply_nested_calls_when_scanning_then_time_is_linear_and_nothing_overf ); } } + +#[test] +fn given_calls_when_scanning_then_argument_starts_and_failing_call_names_match_python() { + let test_cases = [ + CallBoundsTestCase { + description: "argument starts skip the whitespace before the parenthesis", + sql: "x @ab (1) @\u{e9}(@b())", + expected_args_starts: &[8, 14], + expected_failure: None, + }, + CallBoundsTestCase { + description: "a call without its parenthesis names the failing call", + sql: "@a() @bb c(1)", + expected_args_starts: &[3], + expected_failure: Some((Some(5), Some("bb"), ScanError::MissingParenthesis)), + }, + CallBoundsTestCase { + description: "an unclosed call after non-ASCII text names the failing call", + sql: "'\u{e9}' @c\u{e9}(1", + expected_args_starts: &[], + expected_failure: Some((Some(4), Some("c\u{e9}"), ScanError::UnclosedParenthesis)), + }, + CallBoundsTestCase { + description: "a failure between calls names none", + sql: "@a(1) 'open", + expected_args_starts: &[3], + expected_failure: Some((None, None, ScanError::UnclosedQuote)), + }, + ]; + for test_case in test_cases { + let scan = scan_macro_call_sites(python_312(), test_case.sql); + + assert_eq!( + ( + scan.sites + .iter() + .map(|site| site.args_start) + .collect::>(), + scan.failure + .map(|failure| (failure.call_start, failure.call_name, failure.error)), + ), + ( + test_case.expected_args_starts.to_vec(), + test_case.expected_failure.map(|(start, name, error)| ( + start, + name.map(str::to_owned), + error + )), + ), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs index 88311fbbcf..f6196dad72 100644 --- a/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs +++ b/crates/sqlbuild-render/src/macro_calls/tests/test_types.rs @@ -58,3 +58,10 @@ pub(crate) struct MacroCallNamesTestCase { pub(crate) expected_top_level: ExpectedNames, pub(crate) expected_nested: ExpectedNames, } + +pub(crate) struct CallBoundsTestCase { + pub(crate) description: &'static str, + pub(crate) sql: &'static str, + pub(crate) expected_args_starts: &'static [usize], + pub(crate) expected_failure: Option<(Option, Option<&'static str>, ScanError)>, +} diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 78764d5835..f680100238 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -325,7 +325,10 @@ def interpolate_sql_batch( ]: ... def scan_macro_call_sites( sql: str, python_version: tuple[int, int], unicode_version: str -) -> tuple[list[tuple[int, int, str, list[str] | None, bool]], tuple[int | None, str] | None]: ... +) -> tuple[ + list[tuple[int, int, int, str, list[str] | None, bool]], + tuple[int | None, str | None, str] | None, +]: ... def macro_call_names(sql: str, nested: bool, /) -> tuple[list[str] | None, str | None]: ... def splice_macro_calls( sql: str, sites: list[tuple[int, int]], outputs: list[str] diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index 084fac16d6..783a795e89 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -3,17 +3,18 @@ from __future__ import annotations from pathlib import Path +from typing import cast from sqlbuild.compiler.compile._helpers.render.macros import ( - _evaluate_macro_call, _expansion_declarations, _record_macro_declaration_usage, _record_macro_use, _report_generated_references, + evaluate_macro_call, + raise_failed_macro_call, ) from sqlbuild.compiler.compile.classes.macro_expansion_facts import MacroExpansionFacts from sqlbuild.compiler.compile.constants import MACRO_TOKEN -from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( DeclarationResolutionContext, ExpansionSpan, @@ -35,7 +36,6 @@ MacroCallClass, MacroCallScan, MacroCallSite, - MacroScanFailure, ) from sqlbuild.compiler.macro_bridge.types import MacroCallEvent, MacroCallRecord from sqlbuild.compiler.scopes.models import DeclarationIdentity @@ -130,29 +130,15 @@ def _bridged_call_output( # noqa: PLR0913 known_relations: int = len(facts.relations) _ = facts.begin_event_log() try: - macro_result: object - next_index: int - macro_result, next_index = _evaluate_macro_call( + macro_result: str = _top_level_output( sql=sql, - call_start_index=site.start, - file_path=consumer_path, + consumer_path=consumer_path, state=state, declarations=declarations, - stack=(), - top_level=True, + site=site, ) finally: events, replayable = facts.end_event_log() - if not isinstance(macro_result, str): - raise CompileInputError( - f"Macro '@{site.name}' in '{consumer_path}' must return a SQL string when used " - "directly in SQL" - ) - if next_index != site.end: - raise NativeStageMismatchError( - f"Native macro call scan of '{consumer_path}' ended at {site.end}, Python at " - f"{next_index}" - ) added_relations: tuple[SqlResourceRef, ...] = tuple(facts.relations)[known_relations:] if replayable and (prior_relations is not None or not added_relations): bridge.record( @@ -202,43 +188,15 @@ def _unmemoised_sql_macros( for site in scan.sites ] if scan.failure is not None: - _raise_scan_failure( - sql=sql, - consumer_path=consumer_path, + raise_failed_macro_call( + failure=scan.failure, + file_path=consumer_path, state=state, declarations=declarations, - failure=scan.failure, ) return splice_macro_calls(sql=sql, sites=scan.sites, outputs=outputs) -def _raise_scan_failure( - *, - sql: str, - consumer_path: Path, - state: MacroExpansionState, - declarations: DeclarationResolutionContext | None, - failure: MacroScanFailure, -) -> None: - """Raise where Python's scan raises: from the failing call's evaluation, or between calls.""" - - if failure.call_start is None: - raise CompileInputError(failure.message) - _ = _evaluate_macro_call( - sql=sql, - call_start_index=failure.call_start, - file_path=consumer_path, - state=state, - declarations=declarations, - stack=(), - top_level=True, - ) - raise NativeStageMismatchError( - f"Native macro call scan of '{consumer_path}' found no complete call at " - f"{failure.call_start}; Python evaluated one" - ) - - def _mocked_call_output( *, sql: str, consumer_path: Path, state: MacroExpansionState, site: MacroCallSite ) -> str: @@ -264,28 +222,36 @@ def _evaluated_call_output( ) -> str: """Evaluate one natively scanned call in Python without the memo.""" - macro_result: object - next_index: int - macro_result, next_index = _evaluate_macro_call( + macro_result: str = _top_level_output( + sql=sql, + consumer_path=consumer_path, + state=state, + declarations=declarations, + site=site, + ) + return state.facts.render_relation_placeholders(macro_result) + + +def _top_level_output( + *, + sql: str, + consumer_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, + site: MacroCallSite, +) -> str: + """One top-level call's SQL; a non-string result raises as Python's top-level check does.""" + + macro_result: object = evaluate_macro_call( sql=sql, - call_start_index=site.start, + site=site, file_path=consumer_path, state=state, declarations=declarations, stack=(), top_level=True, ) - if not isinstance(macro_result, str): - raise CompileInputError( - f"Macro '@{site.name}' in '{consumer_path}' must return a SQL string when used " - "directly in SQL" - ) - if next_index != site.end: - raise NativeStageMismatchError( - f"Native macro call scan of '{consumer_path}' ended at {site.end}, Python at " - f"{next_index}" - ) - return state.facts.render_relation_placeholders(macro_result) + return cast(str, macro_result) def _replay_macro_call_event( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macros.py b/src/sqlbuild/compiler/compile/_helpers/render/macros.py index 3cde582fbc..b039839358 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macros.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macros.py @@ -6,7 +6,6 @@ import hashlib import heapq import inspect -import re import sys import threading from collections.abc import Callable, Iterator, Mapping @@ -14,7 +13,7 @@ from dataclasses import dataclass, replace from pathlib import Path from types import MappingProxyType, ModuleType -from typing import cast +from typing import NoReturn, cast import sqlbuild._native as _native from sqlbuild.compiler.compile._helpers.explicit_references.macro_arguments import ( @@ -29,8 +28,6 @@ from sqlbuild.compiler.compile.constants import ( DECLARATION_REFERENCE_NAMES, MACRO_CONTEXT_PARAMETER_NAME, - SQL_OPEN_PAREN_TOKEN, - SQL_QUOTE_TOKENS, ) from sqlbuild.compiler.compile.exceptions import ( CompileInputError, @@ -62,7 +59,9 @@ GENERATED_SQL_EVENT, MACRO_USE_EVENT, ) +from sqlbuild.compiler.macro_bridge.main._scan_macro_calls import scan_macro_calls from sqlbuild.compiler.macro_bridge.main.active_macro_bridge import active_macro_bridge +from sqlbuild.compiler.macro_bridge.models import MacroCallScan, MacroCallSite, MacroScanFailure from sqlbuild.compiler.resource_names.main._validate_resource_identity import ( validate_resource_identity, ) @@ -74,25 +73,11 @@ VisibilityRecord, ) from sqlbuild.compiler.scopes.types import DeclarationKind, ResourceKind, ScopeKind, UsageKind -from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren -from sqlbuild.compiler.sql_analysis.main._is_identifier_character import ( - is_identifier_character as _is_identifier_continue, -) -from sqlbuild.compiler.sql_analysis.main._is_identifier_start import ( - is_identifier_start as _is_identifier_start, -) -from sqlbuild.compiler.sql_analysis.main._skip_block_comment import skip_block_comment -from sqlbuild.compiler.sql_analysis.main._skip_line_comment import skip_line_comment -from sqlbuild.compiler.sql_analysis.main._skip_quoted_text import skip_quoted_text from sqlbuild.python_nodes.models import RELATION_RENDERER, SqlResourceRef from sqlbuild.sql_values.models import SqlValue from sqlbuild.sql_values.types import SqlValueKind -_CONTEXT: str = "Macro expansion" -_LINE_COMMENT_TOKEN: str = "--" -_BLOCK_COMMENT_TOKEN: str = "/*" _STAR_IMPORT_NAME: str = "*" -_MACRO_SCAN_PATTERN: re.Pattern[str] = re.compile(r"@|--|/\*|'|\"|`|\$") _SYNTHETIC_PACKAGE_PREFIX: str = "_sqlbuild_project_macros_" _OBJECT_ENTRY_LENGTH: int = 2 _MACRO_MODULE_LOAD_LOCK: threading.RLock = threading.RLock() @@ -1288,23 +1273,37 @@ def _native_macro_call_names(*, sql: str, nested: bool) -> tuple[str, ...]: return tuple(names) -def _evaluate_macro_call( +def raise_failed_macro_call( *, - sql: str, - call_start_index: int, + failure: MacroScanFailure, file_path: Path, state: MacroExpansionState, declarations: DeclarationResolutionContext | None, - stack: tuple[DeclarationIdentity, ...], - top_level: bool, -) -> tuple[object, int]: - macro_name: str = _parse_macro_name(sql=sql, call_start_index=call_start_index) +) -> NoReturn: + """Raise where Python's scan raises: the failing call's macro lookup error, else the scan's.""" + + if failure.call_name is not None: + _ = _resolved_macro( + macro_name=failure.call_name, + file_path=file_path, + state=state, + declarations=declarations, + ) + raise CompileInputError(failure.message) + + +def _resolved_macro( + *, + macro_name: str, + file_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, +) -> LoadedMacro: loaded_macro: LoadedMacro | None = ( declarations.macros.get(macro_name) if declarations is not None else state.loaded_macros.get(macro_name) ) - override_value: str | None = state.macro_overrides.get(macro_name) if declarations is not None and macro_name in declarations.inaccessible_macros: record: DeclarationRecord = declarations.inaccessible_macros[macro_name] suggestions: str = ", ".join(sorted(declarations.macros)) or "none" @@ -1321,17 +1320,31 @@ def _evaluate_macro_call( f"Unknown macro '@{macro_name}' in '{file_path}'. Available macros: " f"{available_macro_names}" ) - opening_paren_index: int = _skip_whitespace( - sql=sql, start_index=call_start_index + 1 + len(macro_name) - ) - closing_paren_index: int = _find_matching_paren( - sql=sql, opening_paren_index=opening_paren_index + return loaded_macro + + +def evaluate_macro_call( + *, + sql: str, + site: MacroCallSite, + file_path: Path, + state: MacroExpansionState, + declarations: DeclarationResolutionContext | None, + stack: tuple[DeclarationIdentity, ...], + top_level: bool, +) -> object: + """Run one natively scanned call: its nested calls, then the macro, in Python's order.""" + + macro_name: str = site.name + loaded_macro: LoadedMacro = _resolved_macro( + macro_name=macro_name, file_path=file_path, state=state, declarations=declarations ) + override_value: str | None = state.macro_overrides.get(macro_name) if override_value is not None: _validate_final_macro_sql( macro_name=macro_name, file_path=file_path, macro_result=override_value ) - return override_value, closing_paren_index + 1 + return override_value identity: DeclarationIdentity = ( declarations.macro_records[macro_name].identity if declarations is not None @@ -1353,7 +1366,7 @@ def _evaluate_macro_call( declarations=declarations, stack=stack, ) - args_source: str = sql[opening_paren_index + 1 : closing_paren_index] + args_source: str = sql[site.args_start : site.end - 1] args: tuple[object, ...] kwargs: dict[str, object] _ = state.facts.open_call_site() @@ -1367,7 +1380,7 @@ def _evaluate_macro_call( stack=stack, ) except MacroArgumentError as error: - raise error.shifted(opening_paren_index + 1) from None + raise error.shifted(site.args_start) from None finally: approved: frozenset[SqlResourceRef] = state.facts.close_call_site() try: @@ -1416,7 +1429,7 @@ def _evaluate_macro_call( file_path=file_path, state=state, ) - return macro_result, closing_paren_index + 1 + return macro_result def _record_macro_use( @@ -1696,22 +1709,24 @@ def _parse_macro_arguments( ) -> tuple[tuple[object, ...], dict[str, object]]: if not args_source.strip(): return (), {} - nested: list[tuple[int, int]] = [] - nested_values: list[object] = [] - cursor: int = 0 - while (start := _find_next_macro_start(sql=args_source, start_index=cursor)) is not None: - nested_value: object - nested_value, cursor = _evaluate_macro_call( + scan: MacroCallScan = scan_macro_calls(sql=args_source) + nested: list[tuple[int, int]] = [(site.start, site.end) for site in scan.sites] + nested_values: list[object] = [ + evaluate_macro_call( sql=args_source, - call_start_index=start, + site=site, file_path=file_path, state=state, declarations=declarations, stack=stack, top_level=False, ) - nested.append((start, cursor)) - nested_values.append(nested_value) + for site in scan.sites + ] + if scan.failure is not None: + raise_failed_macro_call( + failure=scan.failure, file_path=file_path, state=state, declarations=declarations + ) parsed: ParsedMacroArguments = parse_macro_call_arguments( args_source=args_source, nested=tuple(nested), @@ -1721,55 +1736,3 @@ def _parse_macro_arguments( ) state.facts.record_call_site_refs(parsed.typed_references) return parsed.args, parsed.kwargs - - -def _find_next_macro_start(*, sql: str, start_index: int) -> int | None: - index: int = start_index - while True: - match: re.Match[str] | None = _MACRO_SCAN_PATTERN.search(sql, index) - if match is None: - return None - index = match.start() - token: str = match.group() - if token in SQL_QUOTE_TOKENS: - index = skip_quoted_text(sql=sql, start=index, context=_CONTEXT) - continue - if token == _LINE_COMMENT_TOKEN: - index = skip_line_comment(sql=sql, start=index) - continue - if token == _BLOCK_COMMENT_TOKEN: - index = skip_block_comment(sql=sql, start=index, context=_CONTEXT) - continue - if _is_macro_call_start(sql=sql, at_index=index): - return index - index += 1 - - -def _is_macro_call_start(*, sql: str, at_index: int) -> bool: - if at_index + 1 >= len(sql) or not _is_identifier_start(sql[at_index + 1]): - return False - cursor: int = at_index + 2 - while cursor < len(sql) and _is_identifier_continue(sql[cursor]): - cursor += 1 - cursor = _skip_whitespace(sql=sql, start_index=cursor) - return cursor < len(sql) and sql[cursor] == SQL_OPEN_PAREN_TOKEN - - -def _parse_macro_name(*, sql: str, call_start_index: int) -> str: - cursor: int = call_start_index + 1 - while cursor < len(sql) and _is_identifier_continue(sql[cursor]): - cursor += 1 - return sql[call_start_index + 1 : cursor] - - -def _find_matching_paren(*, sql: str, opening_paren_index: int) -> int: - if opening_paren_index >= len(sql) or sql[opening_paren_index] != SQL_OPEN_PAREN_TOKEN: - raise CompileInputError("expected opening parenthesis") - return find_matching_paren(sql=sql, open_paren_index=opening_paren_index, context=_CONTEXT) - - -def _skip_whitespace(*, sql: str, start_index: int) -> int: - index: int = start_index - while index < len(sql) and sql[index].isspace(): - index += 1 - return index diff --git a/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py b/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py index 5d704f0da9..477f3a12e7 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py +++ b/src/sqlbuild/compiler/macro_bridge/main/_scan_macro_calls.py @@ -17,8 +17,8 @@ def scan_macro_calls( ) -> MacroCallScan: """The complete top-level call sites of `sql`, and where its scan raises, if it does.""" - rows: list[tuple[int, int, str, list[str] | None, bool]] - failure: tuple[int | None, str] | None + rows: list[tuple[int, int, int, str, list[str] | None, bool]] + failure: tuple[int | None, str | None, str] | None rows, failure = _native.scan_macro_call_sites( sql, python_version or (sys.version_info[0], sys.version_info[1]), @@ -28,14 +28,15 @@ def scan_macro_calls( sites=tuple( MacroCallSite( start=start, + args_start=args_start, end=end, name=name, tree_names=None if tree_names is None else tuple(tree_names), typed_reference_text=typed_reference_text, ) - for start, end, name, tree_names, typed_reference_text in rows + for start, args_start, end, name, tree_names, typed_reference_text in rows ), failure=None if failure is None - else MacroScanFailure(call_start=failure[0], message=failure[1]), + else MacroScanFailure(call_start=failure[0], call_name=failure[1], message=failure[2]), ) diff --git a/src/sqlbuild/compiler/macro_bridge/models.py b/src/sqlbuild/compiler/macro_bridge/models.py index 9bafeeb6f5..657fdf907d 100644 --- a/src/sqlbuild/compiler/macro_bridge/models.py +++ b/src/sqlbuild/compiler/macro_bridge/models.py @@ -11,6 +11,7 @@ class MacroCallSite: """One top-level macro call in an authored string, in Python string offsets.""" start: int + args_start: int end: int name: str tree_names: tuple[str, ...] | None @@ -23,6 +24,7 @@ class MacroScanFailure: """Where Python's scan of a string raises: in the call at `call_start`, or between calls.""" call_start: int | None + call_name: str | None message: str diff --git a/tests/goldens/python_oracle/macro_expansion_nested_argument_scan_edges.json b/tests/goldens/python_oracle/macro_expansion_nested_argument_scan_edges.json new file mode 100644 index 0000000000..54a611d00c --- /dev/null +++ b/tests/goldens/python_oracle/macro_expansion_nested_argument_scan_edges.json @@ -0,0 +1,74 @@ +[ +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"ff90ea8ba463db2d44e11898" +], +[ +"macro_expansion", +"92b6f68fdb316b8cc086eaa8" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"93f71518378ecde91e3e8c8d" +], +[ +"macro_expansion", +"7c07f5f9f04229f65bb035d7" +], +[ +"macro_expansion", +"bda69b2853d37294e288efba" +], +[ +"macro_expansion", +"ac936e3b930d7120425ccf2d" +], +[ +"macro_expansion", +"1dcc828b6a4485636055d0b7" +], +[ +"macro_expansion", +"1d9a010df82079fdb5727766" +], +[ +"macro_expansion", +"1d9a010df82079fdb5727766" +], +[ +"macro_expansion", +"bda69b2853d37294e288efba" +], +[ +"macro_expansion", +"ffa947079a883f1805686b58" +], +[ +"macro_expansion", +"157bff5a86f07eea0bf7b8d1" +], +[ +"macro_expansion", +"829fcd207b176ca0a12986da" +], +[ +"macro_expansion", +"ea0e6e81338954cb22c3c4dc" +], +[ +"macro_expansion", +"8ac37804f4d743d119f825ac" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py index 139b19a344..bddab3ba58 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py @@ -30,6 +30,15 @@ class MacroBridgeFailureTestCase: expected_message_fragment: str +@dataclass(frozen=True) +class MacroExpansionEdgeTestCase: + """Hand-picked SQL texts whose expansion must match Python's expansion recorded in a golden.""" + + description: str + texts: tuple[str, ...] + expected_mismatches: list[tuple[object, object, object]] + + @dataclass(frozen=True) class MacroExpansionDifferentialTestCase: """Seeded random SQL text that must expand, with and without the bridge, as Python's diff --git a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py index 3822197d2b..d25c4cbe83 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py +++ b/tests/integration/src/sqlbuild/compiler/compile/test_native_macro_bridge.py @@ -16,6 +16,7 @@ MacroBridgeMemoTestCase, MacroBridgeParityTestCase, MacroExpansionDifferentialTestCase, + MacroExpansionEdgeTestCase, ) from tests.integration.src.sqlbuild.compiler.compile.helpers import ( MACRO_BRIDGE_PROJECT_FILES, @@ -223,6 +224,57 @@ def test_given_random_macro_sql_when_expanding_through_bridge_then_matches_pytho assert bridge.stats()[0] > 0 +@pytest.mark.parametrize( + "test_case", + [ + MacroExpansionEdgeTestCase( + description="nested argument scan edges", + texts=( + "@n(@nope(1))", + "@n(@m(1)", + "@n(@ab c(1))", + "@n(@m('x), 1)", + "@n(@m(1), 'oops)", + "@n(@mm (2), @m('é'))", + "@n(@é(1))", + "@n(/* @m( */ @m(1))", + "@n(@nope(1), @m(')", + "@n(@m(1) @xy z(2))", + "@n(@m(1) @mm z(2))", + "@mm z(2)", + "@n($$ ) $$, @m(1))", + '@n("@m(1)")', + "@n(@n(@n(@nope())))", + "@n(@m(1), -- )\n @m(2))", + "@n(@m(1), /* open)", + "@n(@m(@n(@m('é'))))", + ), + expected_mismatches=[], + ), + ], + ids=lambda case: case.description, +) +def test_given_nested_macro_arguments_when_expanding_then_matches_python_golden( + test_case: MacroExpansionEdgeTestCase, +) -> None: + bridge: MacroBridge = MacroBridge( + python_version=(sys.version_info[0], sys.version_info[1]), + unicode_version=unicodedata.unidata_version, + ) + unbridged: list[tuple[object, ...]] = [ + expansion_outcome(sql=sql, bridge=None) for sql in test_case.texts + ] + bridged: list[tuple[object, ...]] = [ + expansion_outcome(sql=sql, bridge=bridge) for sql in test_case.texts + ] + expected: list[GoldenEntry] = read_golden(golden_name("macro_expansion", test_case.description)) + + assert ( + golden_differences(expected, [golden_entry("macro_expansion", item) for item in bridged]), + golden_differences(expected, [golden_entry("macro_expansion", item) for item in unbridged]), + ) == (test_case.expected_mismatches, test_case.expected_mismatches) + + @pytest.mark.parametrize( "test_case", [ From 20fdbb7900fd93d4ed5190780da67bafb795fcda Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:55:48 +0100 Subject: [PATCH 23/36] feat(native): read fixture projection facts natively Literal types, NULL literals and quoted aliases of a fixture query's first SELECT come from the native parse instead of the wheel's to_dict walk in Python. A golden recorded from Python's _simple_projection_facts covers literals, structured values, quoted non-ASCII aliases, nested and set-operation selects and parse errors. --- .../analysis_session/_helpers/dict_walk.rs | 138 +++++++++++++++++- .../assembly/analysis_session/constants.rs | 21 +++ .../src/assembly/analysis_session/main/mod.rs | 1 + .../analysis_session/main/projection_facts.rs | 9 ++ .../src/assembly/analysis_session/models.rs | 9 ++ .../analysis_session/query_columns.rs | 21 ++- src/sqlbuild/_native.pyi | 3 + .../_helpers/analysis/fixture_columns.py | 110 +------------- src/sqlbuild/compiler/compile/constants.py | 4 - .../compiler/sql_analysis/constants.py | 1 - ...ection_facts_fixture_projection_edges.json | 66 +++++++++ .../compiler/analysis_session/_test_types.py | 9 ++ .../compiler/analysis_session/helpers.py | 9 ++ .../test_native_projection_facts.py | 73 +++++++++ 14 files changed, 361 insertions(+), 113 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/projection_facts.rs create mode 100644 tests/goldens/python_oracle/projection_facts_fixture_projection_edges.json create mode 100644 tests/integration/src/sqlbuild/compiler/analysis_session/test_native_projection_facts.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs index f1a4d18ad2..b0c8573ed6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs @@ -8,10 +8,14 @@ use sqlbuild_core::text::main::active_python_text::active_python_text; use sqlbuild_core::text::main::python_casefold::python_casefold; use sqlbuild_core::text::main::python_upper::python_upper; +use crate::assembly::analysis_session::models::ProjectionFacts; + use crate::assembly::analysis_session::constants::{ - ALIAS_AST_KIND, ANNOTATED_AST_KIND, COLUMN_AST_KIND, DEPENDENCY_FUNCTION_NAMES, - FUNCTION_AST_KIND, MAX_FUNCTION_CALL_DEPTH, RENDERED_TYPE_NAMES, STAR_AST_KIND, TABLE_AST_KIND, - WILDCARD, + ALIAS_AST_KIND, ANNOTATED_AST_KIND, ARRAY_FIXTURE_TYPE, ARRAY_PAYLOAD_KIND, COLUMN_AST_KIND, + DEPENDENCY_FUNCTION_NAMES, FUNCTION_AST_KIND, FUNCTION_FIXTURE_TYPES, FUNCTION_PAYLOAD_KIND, + LITERAL_FIXTURE_TYPES, LITERAL_PAYLOAD_KIND, MAX_FUNCTION_CALL_DEPTH, NULL_PAYLOAD_KIND, + RENDERED_TYPE_NAMES, SELECT_PAYLOAD_KEY, STAR_AST_KIND, STRUCTURED_PAYLOAD_KINDS, + TABLE_AST_KIND, VARIANT_FIXTURE_TYPE, WILDCARD, }; /// Python's `bool(value)`. @@ -413,3 +417,131 @@ pub(crate) fn where_clause(node: &Expression) -> Result, String> { .map(|clause| serde_json::to_value(clause).map_err(|error| error.to_string())) .transpose() } + +/// Python's `_simple_projection_facts` of the first `SELECT`; empty where the parse fails. +pub(crate) fn fixture_projection_facts( + sql: &str, + dialect: Option<&str>, +) -> Result { + let dialect: &str = dialect.filter(|name| !name.is_empty()).unwrap_or("generic"); + let Some(parsed) = parse_one(sql, dialect)? else { + return Ok(ProjectionFacts::default()); + }; + let payload: Value = to_dict(&parsed)?; + let Some(select) = first_select(&payload) else { + return Ok(ProjectionFacts::default()); + }; + let mut facts: ProjectionFacts = collected_facts(select); + let direct: bool = matches!( + payload + .as_object() + .and_then(|map| map.get(SELECT_PAYLOAD_KEY)), + Some(Value::Object(_)) + ); + if !direct { + facts.null_literal_names.clear(); + } + Ok(facts) +} + +/// Python's recursive `_first_select`, depth first in payload order, without recursion. +fn first_select(payload: &Value) -> Option<&Map> { + let mut pending: Vec<&Value> = vec![payload]; + while let Some(node) = pending.pop() { + match node { + Value::Object(map) => { + if let Some(Value::Object(select)) = map.get(SELECT_PAYLOAD_KEY) { + return Some(select); + } + pending.extend(map.values().rev()); + } + Value::Array(items) => pending.extend(items.iter().rev()), + _ => {} + } + } + None +} + +fn collected_facts(select: &Map) -> ProjectionFacts { + let python = active_python_text(); + let mut facts: ProjectionFacts = ProjectionFacts::default(); + let mut quoted: Vec = Vec::new(); + let mut nulls: Vec = Vec::new(); + let expressions: &[Value] = select + .get("expressions") + .and_then(Value::as_array) + .map_or(&[], Vec::as_slice); + for expression in expressions { + let Some(Value::Object(alias_payload)) = expression.get("alias") else { + continue; + }; + let (Some(Value::Object(alias)), Some(Value::Object(value))) = + (alias_payload.get("alias"), alias_payload.get("this")) + else { + continue; + }; + let Some(Value::String(name)) = alias.get("name") else { + continue; + }; + let folded: String = python_casefold(python, name); + if alias.get("quoted") == Some(&Value::Bool(true)) { + quoted.push(folded.clone()); + } + let Some((kind, value_payload)) = value.iter().next() else { + continue; + }; + if kind == NULL_PAYLOAD_KIND { + nulls.push(folded); + continue; + } + if let Some(inferred) = simple_type(kind, value_payload) { + facts.types.push((folded, inferred.to_owned())); + } + } + facts.quoted_names = unique(quoted); + facts.null_literal_names = unique(nulls); + facts +} + +/// Python's `_simple_expression_type`. +fn simple_type(kind: &str, payload: &Value) -> Option<&'static str> { + if kind == LITERAL_PAYLOAD_KIND { + let literal_type: &str = payload.get("literal_type")?.as_str()?; + return lookup(&LITERAL_FIXTURE_TYPES, literal_type); + } + if kind == ARRAY_PAYLOAD_KIND { + return Some(ARRAY_FIXTURE_TYPE); + } + if STRUCTURED_PAYLOAD_KINDS.contains(&kind) { + return Some(VARIANT_FIXTURE_TYPE); + } + if kind == FUNCTION_PAYLOAD_KIND { + let name: &str = payload + .as_object()? + .get("name") + .and_then(Value::as_str) + .unwrap_or_default(); + return lookup( + &FUNCTION_FIXTURE_TYPES, + &python_upper(active_python_text(), name), + ); + } + None +} + +fn lookup(table: &[(&str, &'static str)], key: &str) -> Option<&'static str> { + table + .iter() + .find(|(candidate, _)| *candidate == key) + .map(|(_, value)| *value) +} + +fn unique(names: Vec) -> Vec { + let mut kept: Vec = Vec::with_capacity(names.len()); + for name in names { + if !kept.contains(&name) { + kept.push(name); + } + } + kept +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index a312ce04a6..16817bdaa0 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -206,3 +206,24 @@ pub(crate) const VARINT_PAYLOAD_MASK: u8 = 0x7f; pub(crate) const VARINT_PAYLOAD_BITS: u32 = 7; /// The most bytes one stored `u64` varint takes. pub(crate) const VARINT_MAX_BYTES: usize = 10; +/// Payload kinds and literal types Python's fixture projection facts read. +pub(crate) const NULL_PAYLOAD_KIND: &str = "null"; +pub(crate) const LITERAL_PAYLOAD_KIND: &str = "literal"; +pub(crate) const ARRAY_PAYLOAD_KIND: &str = "array_func"; +pub(crate) const STRUCTURED_PAYLOAD_KINDS: [&str; 2] = ["parse_json", "struct"]; +pub(crate) const FUNCTION_PAYLOAD_KIND: &str = "function"; +pub(crate) const SELECT_PAYLOAD_KEY: &str = "select"; +/// `{literal_type: type}` Python's `_simple_expression_type` maps a literal to. +pub(crate) const LITERAL_FIXTURE_TYPES: [(&str, &str); 3] = [ + ("string", "VARCHAR"), + ("number", "NUMBER"), + ("boolean", "BOOLEAN"), +]; +/// `{function name: type}` Python's `_simple_expression_type` maps a function to. +pub(crate) const FUNCTION_FIXTURE_TYPES: [(&str, &str); 3] = [ + ("ARRAY_CONSTRUCT", "ARRAY"), + ("OBJECT_CONSTRUCT", "OBJECT"), + ("PARSE_JSON", "VARIANT"), +]; +pub(crate) const ARRAY_FIXTURE_TYPE: &str = "ARRAY"; +pub(crate) const VARIANT_FIXTURE_TYPE: &str = "VARIANT"; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 3abe4d2a9f..97d081ef54 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -3,6 +3,7 @@ pub mod expression_shapes; pub mod finish_analysis_session; pub mod finished_fact_models; pub mod finished_model_facts; +pub mod projection_facts; pub mod prove_dynamic_contracts; pub mod prove_finished_dynamic_contracts; pub mod query_columns; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/projection_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/projection_facts.rs new file mode 100644 index 0000000000..ada48a169a --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/projection_facts.rs @@ -0,0 +1,9 @@ +//! Python's `_simple_projection_facts`: literal types, NULL literals and quoted aliases of a query. + +use crate::assembly::analysis_session::_helpers::dict_walk::fixture_projection_facts; +use crate::assembly::analysis_session::models::ProjectionFacts; + +/// The facts of the first `SELECT`; empty where the wheel's parse raises `PolyglotError`. +pub fn projection_facts(sql: &str, dialect: Option<&str>) -> Result { + fixture_projection_facts(sql, dialect) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index fb54ef0242..a0c8fc0593 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -389,3 +389,12 @@ impl fmt::Debug for NullabilityCallback { formatter.write_str("NullabilityCallback") } } + +/// Python's `_simple_projection_facts` of a fixture query; names are casefolded. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProjectionFacts { + /// `(name, type)` in projection order; a later projection of a name wins. + pub types: Vec<(String, String)>, + pub null_literal_names: Vec, + pub quoted_names: Vec, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs index 1aee467db3..65fbdf1cba 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs @@ -3,9 +3,11 @@ use pyo3::prelude::{Bound, Py, PyAny, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::types::PyDict; use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::assembly::analysis_session::main::projection_facts::projection_facts; use sqlbuild_analysis::assembly::analysis_session::main::query_columns::query_columns; use sqlbuild_analysis::assembly::analysis_session::models::{ - ColumnFact, QueryColumns, QueryColumnsInput, QueryColumnsMode, QueryColumnsRequest, + ColumnFact, ProjectionFacts, QueryColumns, QueryColumnsInput, QueryColumnsMode, + QueryColumnsRequest, }; use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; use sqlbuild_core::panics::main::native_failure::native_failure; @@ -18,6 +20,7 @@ use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; const QUERY_COLUMNS_CONTEXT: &str = "native query column analysis"; +const PROJECTION_FACTS_CONTEXT: &str = "native fixture projection facts"; /// `(sql, placeholders, analysis names, lineage references, recover CTE facts, mode)`. type QueryRow = ( @@ -30,6 +33,8 @@ type QueryRow = ( ); /// `(dialect, function return types, nullability rules, types, nullability, queries)`. type RequestRow = (String, Pairs, Pairs, Shapes, Shapes, Vec); +/// `([(name, type)], NULL literal names, quoted names)`. +type ProjectionFactsRow = (Vec<(String, String)>, Vec, Vec); /// `(succeeded, [(name, type, nullability)] or None, has star)`. type ColumnsRow = (bool, Option, String)>>, bool); @@ -101,8 +106,22 @@ fn columns_row(columns: QueryColumns) -> ColumnsRow { ) } +/// Python's `_simple_projection_facts`: `(types, NULL literal names, quoted names)`, casefolded. +#[pyfunction] +fn fixture_projection_facts( + py: Python<'_>, + sql: &str, + dialect: Option<&str>, +) -> PyResult { + let facts: ProjectionFacts = py + .compiler_detach(|| projection_facts(sql, dialect)) + .map_err(|reason| compiler_error(native_failure(PROJECTION_FACTS_CONTEXT, &reason)))?; + Ok((facts.types, facts.null_literal_names, facts.quoted_names)) +} + pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(infer_query_columns, module)?)?; + module.add_function(wrap_pyfunction!(fixture_projection_facts, module)?)?; Ok(()) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index f680100238..c482a021ea 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -329,6 +329,9 @@ def scan_macro_call_sites( list[tuple[int, int, int, str, list[str] | None, bool]], tuple[int | None, str | None, str] | None, ]: ... +def fixture_projection_facts( + sql: str, dialect: str | None, / +) -> tuple[list[tuple[str, str]], list[str], list[str]]: ... def macro_call_names(sql: str, nested: bool, /) -> tuple[list[str] | None, str | None]: ... def splice_macro_calls( sql: str, sites: list[tuple[int, int]], outputs: list[str] diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py b/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py index 6a6cfe07a4..05b747a64d 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/fixture_columns.py @@ -2,25 +2,16 @@ from __future__ import annotations -from typing import Any, cast - +import sqlbuild._native as _native from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( infer_native_query_columns, ) from sqlbuild.compiler.analysis_session.models import NativeColumnQuery, NativeQueryColumns -from sqlbuild.compiler.compile.constants import ( - POLYGLOT_ARRAY_KIND, - POLYGLOT_FUNCTION_KIND, - POLYGLOT_LITERAL_KIND, - POLYGLOT_STRUCTURED_KINDS, -) from sqlbuild.compiler.compile.models import ( FixtureColumnInference, InferredColumn, ) -from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_KIND_NULL -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql def infer_fixture_column_facts( @@ -55,97 +46,8 @@ def infer_fixture_column_facts( def _simple_projection_facts( *, query_sql: str, dialect: str | None ) -> tuple[dict[str, str], frozenset[str], frozenset[str]]: - polyglot_sql: Any = import_polyglot_sql() - try: - payload: dict[str, Any] = polyglot_sql.parse_one( - query_sql, dialect=dialect or "generic" - ).to_dict() - except polyglot_sql.PolyglotError: - return {}, frozenset(), frozenset() - direct_select_payload: object = payload.get("select") - select_payload: dict[str, Any] | None = _first_select(payload) - if select_payload is None: - return {}, frozenset(), frozenset() - types, null_literal_names, quoted_names = _collect_projection_facts( - select_payload=select_payload - ) - effective_null_literal_names: frozenset[str] = ( - null_literal_names if isinstance(direct_select_payload, dict) else frozenset() - ) - return types, effective_null_literal_names, quoted_names - - -def _collect_projection_facts( - *, select_payload: dict[str, Any] -) -> tuple[dict[str, str], frozenset[str], frozenset[str]]: - types: dict[str, str] = {} - null_literal_names: set[str] = set() - quoted_names: set[str] = set() - for expression in select_payload.get("expressions", []): - alias_payload: object = expression.get("alias") if isinstance(expression, dict) else None - if not isinstance(alias_payload, dict): - continue - alias: object = alias_payload.get("alias") - value: object = alias_payload.get("this") - if not isinstance(alias, dict) or not isinstance(value, dict): - continue - name: object = alias.get("name") - if not isinstance(name, str): - continue - if alias.get("quoted") is True: - quoted_names.add(name.casefold()) - raw_value_kind: object = next(iter(value), "") - if not isinstance(raw_value_kind, str): - continue - value_payload: object = value.get(raw_value_kind) - if raw_value_kind == POLYGLOT_KIND_NULL: - null_literal_names.add(name.casefold()) - continue - inferred_type: str | None = _simple_expression_type( - value_kind=raw_value_kind, - value_payload=value_payload, - ) - if inferred_type is not None: - types[name.casefold()] = inferred_type - return types, frozenset(null_literal_names), frozenset(quoted_names) - - -def _simple_expression_type(*, value_kind: str, value_payload: object) -> str | None: - if value_kind == POLYGLOT_LITERAL_KIND and isinstance(value_payload, dict): - value_dict: dict[object, object] = cast(dict[object, object], value_payload) - return { - "string": "VARCHAR", - "number": "NUMBER", - "boolean": "BOOLEAN", - }.get(str(value_dict.get("literal_type"))) - if value_kind == POLYGLOT_ARRAY_KIND: - return "ARRAY" - if value_kind in POLYGLOT_STRUCTURED_KINDS: - return "VARIANT" - if value_kind == POLYGLOT_FUNCTION_KIND and isinstance(value_payload, dict): - value_dict = cast(dict[object, object], value_payload) - function_name: str = str(value_dict.get("name") or "").upper() - return { - "ARRAY_CONSTRUCT": "ARRAY", - "OBJECT_CONSTRUCT": "OBJECT", - "PARSE_JSON": "VARIANT", - }.get(function_name) - return None - - -def _first_select(payload: object) -> dict[str, Any] | None: - if isinstance(payload, dict): - payload_dict: dict[object, object] = cast(dict[object, object], payload) - select_payload: object = payload_dict.get("select") - if isinstance(select_payload, dict): - return cast(dict[str, Any], select_payload) - for value in payload_dict.values(): - found: dict[str, Any] | None = _first_select(value) - if found is not None: - return found - if isinstance(payload, list): - for value in payload: - found = _first_select(value) - if found is not None: - return found - return None + types: list[tuple[str, str]] + null_literal_names: list[str] + quoted_names: list[str] + types, null_literal_names, quoted_names = _native.fixture_projection_facts(query_sql, dialect) + return dict(types), frozenset(null_literal_names), frozenset(quoted_names) diff --git a/src/sqlbuild/compiler/compile/constants.py b/src/sqlbuild/compiler/compile/constants.py index 78f4df17d4..53a98bf891 100644 --- a/src/sqlbuild/compiler/compile/constants.py +++ b/src/sqlbuild/compiler/compile/constants.py @@ -109,10 +109,6 @@ SQL_INTERPOLATION_TOKEN: str = "@@" TEMPLATE_OPEN_TOKEN: str = "${" -POLYGLOT_LITERAL_KIND: str = "literal" -POLYGLOT_ARRAY_KIND: str = "array_func" -POLYGLOT_STRUCTURED_KINDS: frozenset[str] = frozenset({"parse_json", "struct"}) -POLYGLOT_FUNCTION_KIND: str = "function" MACRO_CALL_PATTERN: re.Pattern[str] = re.compile(r"@[A-Za-z_][A-Za-z0-9_]*\s*\(") SQL_ARGUMENT_QUOTED_PARAMETER_PATTERN: re.Pattern[str] = re.compile( diff --git a/src/sqlbuild/compiler/sql_analysis/constants.py b/src/sqlbuild/compiler/sql_analysis/constants.py index 7cc6861c9d..ab2a88c690 100644 --- a/src/sqlbuild/compiler/sql_analysis/constants.py +++ b/src/sqlbuild/compiler/sql_analysis/constants.py @@ -56,7 +56,6 @@ POLYGLOT_KIND_LITERAL: str = "literal" POLYGLOT_KIND_MAX: str = "max" POLYGLOT_KIND_MIN: str = "min" -POLYGLOT_KIND_NULL: str = "null" POLYGLOT_KIND_PAREN: str = "paren" POLYGLOT_KIND_SELECT: str = "select" POLYGLOT_KIND_STRING_AGG: str = "string_agg" diff --git a/tests/goldens/python_oracle/projection_facts_fixture_projection_edges.json b/tests/goldens/python_oracle/projection_facts_fixture_projection_edges.json new file mode 100644 index 0000000000..bc68922b05 --- /dev/null +++ b/tests/goldens/python_oracle/projection_facts_fixture_projection_edges.json @@ -0,0 +1,66 @@ +[ +[ +"projection_facts", +"6da442e97bb6c306b9c2f819" +], +[ +"projection_facts", +"a1e82c6e27da412dd6c97a45" +], +[ +"projection_facts", +"3eba916f73c3192f9824b709" +], +[ +"projection_facts", +"150e095633f5482a909c0f88" +], +[ +"projection_facts", +"150e095633f5482a909c0f88" +], +[ +"projection_facts", +"8d71ffb25f3a586e8521356b" +], +[ +"projection_facts", +"15340dd7914c4d047a966415" +], +[ +"projection_facts", +"24213e6d5dbdc0914977176a" +], +[ +"projection_facts", +"7f5d3f1706af5144062e2050" +], +[ +"projection_facts", +"150e095633f5482a909c0f88" +], +[ +"projection_facts", +"150e095633f5482a909c0f88" +], +[ +"projection_facts", +"727b5e58480115a8d28d47d5" +], +[ +"projection_facts", +"e2e814e9aa9661dae2c19a7c" +], +[ +"projection_facts", +"150e095633f5482a909c0f88" +], +[ +"projection_facts", +"0481f2753e03358cd1c77e95" +], +[ +"projection_facts", +"1edb014872e2983e44bab225" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 4e075238ee..c15174ae86 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -137,3 +137,12 @@ class MissingSessionAnalysisTestCase: description: str expected_message: str + + +@dataclass(frozen=True) +class ProjectionFactsTestCase: + """Fixture queries whose projection facts Python recorded in a golden.""" + + description: str + queries: tuple[tuple[str, str], ...] + expected_differences: list[tuple[int, str, object, object]] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 3b91bd7dd2..9f149afe19 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -903,3 +903,12 @@ def upstream_lineage_sources( ) trace: list[dict[str, Any]] = json.loads(capsys.readouterr().out)["trace"] return code, [step["source"]["resource_name"] for step in trace] + + +def projection_view( + row: tuple[list[tuple[str, str]], list[str], list[str]], +) -> tuple[dict[str, str], frozenset[str], frozenset[str]]: + """A native projection-facts row as Python's `_simple_projection_facts` returned it.""" + + types, null_literal_names, quoted_names = row + return dict(types), frozenset(null_literal_names), frozenset(quoted_names) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_projection_facts.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_projection_facts.py new file mode 100644 index 0000000000..31c31a7c47 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_projection_facts.py @@ -0,0 +1,73 @@ +"""Fixture projection facts match Python's `_simple_projection_facts` recorded in a golden.""" + +from __future__ import annotations + +import pytest + +import sqlbuild._native as native_module +from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + ProjectionFactsTestCase, +) +from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import projection_view +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) + + +@pytest.mark.parametrize( + "test_case", + [ + ProjectionFactsTestCase( + description="fixture projection edges", + queries=( + ("SELECT 'a' AS name, 1 AS qty, TRUE AS flag, NULL AS missing", "duckdb"), + ( + "SELECT ARRAY_CONSTRUCT(1, 2) AS items, OBJECT_CONSTRUCT('a', 1) AS obj, PARSE_JSON('{}') AS doc, [1, 2] AS arr", + "snowflake", + ), + ('SELECT NULL AS "Quoted", \'x\' AS "Größe", 2 AS STRASSE', "duckdb"), + ( + "WITH base AS (SELECT NULL AS gone, 'v' AS kept) SELECT gone, kept FROM base", + "duckdb", + ), + ("SELECT * FROM (SELECT NULL AS inner_null, 1.5 AS ratio) AS t", "duckdb"), + ("SELECT 'a' AS x UNION ALL SELECT NULL AS x", "duckdb"), + ("SELECT lower('A') AS lowered, CAST(1 AS INT) AS cast_one, 'ß' AS ß", "duckdb"), + ("SELECT 1 AS a, 'dup' AS a", "generic"), + ("SELECT parse_json('[1]') AS p, STRUCT_PACK(a := 1) AS s", "duckdb"), + ("SELECT ( AS broken", "duckdb"), + ("SELECT 1 AS one; SELECT 2 AS two", "duckdb"), + ("SELECT {'a': 1} AS st, NULL AS n", "duckdb"), + ("INSERT INTO t SELECT NULL AS n, 'a' AS s", "duckdb"), + ('SELECT \'é\' AS "ÉTÉ", null AS "İ"', "bigquery"), + ("SELECT 1 AS id", "postgres"), + ("SELECT 'a' AS k", "tsql"), + ), + expected_differences=[], + ) + ], + ids=lambda case: case.description, +) +def test_given_fixture_queries_when_reading_projection_facts_then_matches_python_golden( + test_case: ProjectionFactsTestCase, +) -> None: + views: list[tuple[dict[str, str], frozenset[str], frozenset[str]]] = [ + projection_view(native_module.fixture_projection_facts(sql, dialect)) + for sql, dialect in test_case.queries + ] + expected: list[GoldenEntry] = read_golden( + golden_name("projection_facts", test_case.description) + ) + + assert ( + golden_differences(expected, [golden_entry("projection_facts", view) for view in views]) + == test_case.expected_differences + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) From c8c3951274a01956acc241164730fe62cfc7de61 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:57:50 +0100 Subject: [PATCH 24/36] feat(native): substitute placeholder defaults natively for analysis Python's regex substitute_placeholder_defaults now calls the native placeholder substitution the syntax checks already use, so one implementation serves both. --- .../src/bindings/_helpers/sqltext/mod.rs | 1 + .../bindings/_helpers/sqltext/placeholders.rs | 24 +++++++++++++++++++ .../bindings/_helpers/sqltext/registration.rs | 3 ++- src/sqlbuild/_native.pyi | 1 + .../_helpers/analysis/reference_names.py | 9 ++----- .../_helpers/test_sql_analysis_columns.py | 18 ++++++++++++++ 6 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/sqltext/placeholders.rs diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/mod.rs index 13533443f7..4cd903b971 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod authored_values; pub(crate) mod declaration_references; pub(crate) mod lexical_syntax; pub(crate) mod model_headers; +pub(crate) mod placeholders; pub(crate) mod registration; pub(crate) mod sql_references; pub(crate) mod static_sql; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/placeholders.rs b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/placeholders.rs new file mode 100644 index 0000000000..c65bb50926 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/placeholders.rs @@ -0,0 +1,24 @@ +//! Substitute `@@@name` placeholder defaults for SQL analysis. + +use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; + +use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::types::CompilerDetach; + +/// Python's `substitute_placeholder_defaults`: each `@@@name` becomes its default. +#[pyfunction] +fn substitute_placeholder_defaults( + py: Python<'_>, + sql: &str, + placeholders: Vec<(String, String)>, +) -> PyResult { + py.compiler_detach(|| Ok(placeholder_defaults(sql, &placeholders))) + .map_err(compiler_error) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(substitute_placeholder_defaults, module)?)?; + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/registration.rs b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/registration.rs index 0e0a165845..4fe4b866e4 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/registration.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/registration.rs @@ -3,7 +3,7 @@ use pyo3::prelude::{Bound, PyModule, PyResult}; use crate::bindings::_helpers::sqltext::{ - declaration_references, model_headers, sql_references, static_sql, + declaration_references, model_headers, placeholders, sql_references, static_sql, }; pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { @@ -11,5 +11,6 @@ pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { static_sql::register(module)?; sql_references::register(module)?; declaration_references::register(module)?; + placeholders::register(module)?; Ok(()) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index c482a021ea..654b4b5cc0 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -329,6 +329,7 @@ def scan_macro_call_sites( list[tuple[int, int, int, str, list[str] | None, bool]], tuple[int | None, str | None, str] | None, ]: ... +def substitute_placeholder_defaults(sql: str, placeholders: list[tuple[str, str]], /) -> str: ... def fixture_projection_facts( sql: str, dialect: str | None, / ) -> tuple[list[tuple[str, str]], list[str], list[str]]: ... diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py index 46afcba52b..c7389d31f1 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py @@ -5,6 +5,7 @@ import re from collections.abc import Iterable +import sqlbuild._native as _native from sqlbuild.compiler.compile.constants import ( SQL_QUALIFIER_SEPARATOR_TOKEN, ) @@ -15,7 +16,6 @@ from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql -_PLACEHOLDER_PATTERN: re.Pattern[str] = re.compile(r"@@@(\w+)") _QUALIFIED_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile( r'(?[^"]+)"|`(?P[^`]+)`|' r"\[(?P[^\]]+)\]|(?P[A-Za-z_$][A-Za-z0-9_$]*))" @@ -58,12 +58,7 @@ def substitute_placeholder_defaults(*, query_sql: str, placeholders: dict[str, s if not placeholders: return query_sql - - def _replacer(match: re.Match[str]) -> str: - name: str = match.group(1) - return placeholders.get(name, match.group(0)) - - return _PLACEHOLDER_PATTERN.sub(_replacer, query_sql) + return _native.substitute_placeholder_defaults(query_sql, list(placeholders.items())) def _replace_refs_with_stubs( diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py index 4fbc721fb7..9b54407c9e 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py @@ -915,6 +915,24 @@ def test_given_star_projection_when_compact_analysis_enabled_then_expands_schema placeholders={}, expected_sql="SELECT * FROM t WHERE d >= @@@start", ), + SubstitutePlaceholderDefaultsTestCase( + description="an extra at sign and unknown names stay as Python's regex leaves them", + query_sql="@@@@a + @@@a @@@unknown @@@ a", + placeholders={"a": "1", "é": "2", "x\u0661": "3", "a_b1x": "4"}, + expected_sql="@1 + 1 @@@unknown @@@ a", + ), + SubstitutePlaceholderDefaultsTestCase( + description="names follow Python's word characters beyond ASCII", + query_sql="@@@é·@@@x١ @@@a_b1x@@@", + placeholders={"a": "1", "é": "2", "x\u0661": "3", "a_b1x": "4"}, + expected_sql="2·3 4@@@", + ), + SubstitutePlaceholderDefaultsTestCase( + description="adjacent and quoted placeholders are replaced", + query_sql="@@@a@@@a '@@@a'", + placeholders={"a": "1", "é": "2", "x\u0661": "3", "a_b1x": "4"}, + expected_sql="11 '1'", + ), SubstitutePlaceholderDefaultsTestCase( description="returns sql unchanged when no placeholders in sql", query_sql="SELECT * FROM t", From cf64a1910634bc4c2f4c38143c9ec3d81c2fd06e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:00:40 +0100 Subject: [PATCH 25/36] refactor(compiler): delete the unused Python binding-share and qualified-reference scans Native analysis sessions share binding results themselves, so Python's binding-share prekeys, shared result keys and the qualified-reference scan they used no longer run anywhere, along with the constants only the deleted Python analyses read. --- .../_helpers/analysis/reference_names.py | 72 ---- .../compile/_helpers/sharing/binding.py | 374 ------------------ src/sqlbuild/compiler/compile/constants.py | 31 -- .../compiler/sql_analysis/constants.py | 111 ------ 4 files changed, 588 deletions(-) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py index c7389d31f1..55f5f5ee44 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/reference_names.py @@ -2,13 +2,7 @@ from __future__ import annotations -import re -from collections.abc import Iterable - import sqlbuild._native as _native -from sqlbuild.compiler.compile.constants import ( - SQL_QUALIFIER_SEPARATOR_TOKEN, -) from sqlbuild.compiler.compile.models import ( CompileSqlReference, ) @@ -16,18 +10,6 @@ from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql -_QUALIFIED_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile( - r'(?[^"]+)"|`(?P[^`]+)`|' - r"\[(?P[^\]]+)\]|(?P[A-Za-z_$][A-Za-z0-9_$]*))" - r"(?:\s|--[^\r\n]*(?:\r?\n|$)|/\*.*?\*/)*\.", - re.DOTALL, -) -_QUALIFIED_SCAN_CONSUMING_TOKENS: tuple[str, ...] = ('"', "`", "[", "--", "/*") -_PLAIN_IDENTIFIER_PATTERN: re.Pattern[str] = re.compile(r"[a-z_$][a-z0-9_$]*") -_IDENTIFIER_CHARACTERS: frozenset[str] = frozenset( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$" -) - def _lineage_resource_type(reference: CompileSqlReference) -> CompiledResourceType | None: if reference.ref_kind == SqlReferenceKind.REF: @@ -70,57 +52,3 @@ def _replace_refs_with_stubs( """Replace SQLBuild marker calls with parseable SQL stubs.""" return normalize_analysis_sql(sql=query_sql, dialect=dialect, stubs=relation_stubs) - - -def _qualified_reference_names(*, query_sql: str, reference_names: Iterable[str]) -> frozenset[str]: - """Return qualifier reference names, searching names directly when no token can hide one.""" - - if SQL_QUALIFIER_SEPARATOR_TOKEN not in query_sql: - return frozenset() - names_by_normalized: dict[str, list[str]] = {} - for value in reference_names: - name: str = str(value) - names_by_normalized.setdefault(name.casefold(), []).append(name) - if not names_by_normalized: - return frozenset() - if not query_sql.isascii() or any( - token in query_sql for token in _QUALIFIED_SCAN_CONSUMING_TOKENS - ): - return _scanned_qualified_reference_names( - query_sql=query_sql, names_by_normalized=names_by_normalized - ) - - folded_sql: str = query_sql.lower() - qualified: set[str] = set() - for normalized, names in names_by_normalized.items(): - if _PLAIN_IDENTIFIER_PATTERN.fullmatch(normalized) is not None and _is_qualifier( - folded_sql=folded_sql, name=normalized - ): - qualified.update(names) - return frozenset(qualified) - - -def _is_qualifier(*, folded_sql: str, name: str) -> bool: - length: int = len(folded_sql) - start: int = folded_sql.find(name) - while start != -1: - end: int = start + len(name) - if (start == 0 or folded_sql[start - 1] not in _IDENTIFIER_CHARACTERS) and ( - end == length or folded_sql[end] not in _IDENTIFIER_CHARACTERS - ): - while end < length and folded_sql[end].isspace(): - end += 1 - if end < length and folded_sql[end] == SQL_QUALIFIER_SEPARATOR_TOKEN: - return True - start = folded_sql.find(name, start + 1) - return False - - -def _scanned_qualified_reference_names( - *, query_sql: str, names_by_normalized: dict[str, list[str]] -) -> frozenset[str]: - qualified: set[str] = set() - for match in _QUALIFIED_IDENTIFIER_PATTERN.finditer(query_sql): - identifier: str = next(value for value in match.groups() if value is not None) - qualified.update(names_by_normalized.get(identifier.casefold(), ())) - return frozenset(qualified) diff --git a/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py b/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py index e99be95a28..d337b2e6fd 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py +++ b/src/sqlbuild/compiler/compile/_helpers/sharing/binding.py @@ -2,38 +2,14 @@ from __future__ import annotations -import re -from collections import Counter -from collections.abc import Iterable, Mapping, Sequence -from dataclasses import replace -from typing import Any, cast - from sqlbuild.compiler.compile._helpers.analysis.reference_names import ( _analysis_reference_name, _lineage_resource_type, - _qualified_reference_names, -) -from sqlbuild.compiler.compile.constants import ( - COMPACT_REFERENCE_MARKER_PATTERN, - COMPACT_RELATION_STUB_PREFIX, - COMPACT_UNSTUBBED_REFERENCE_KIND, - MIN_SHARED_BINDING_QUERY_MEMBERS, ) from sqlbuild.compiler.compile.models import ( - CompactBatchContext, - CompactBatchInputs, CompileSqlReference, - ModelSqlAnalysisRequest, - NativeCompactAnalysis, - PreparedBindingQuery, - SharedBindingQuery, ) from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.sql_analysis.constants import SQL_QUOTED_IDENTIFIER_DELIMITER -from sqlbuild.compiler.sql_analysis.main._normalize_analysis_batch import ( - normalize_analysis_sql_results, -) -from sqlbuild.compiler.sql_analysis.models import SqlSchemaValidationRequest def lineage_reference_map( @@ -50,353 +26,3 @@ def lineage_reference_map( reference.ref_name, ) return reference_map - - -def binding_share_prekey(*, query_sql: str, references: tuple[CompileSqlReference, ...]) -> str: - """Return authored SQL with lineage relation markers replaced by their stub positions.""" - - positions: dict[str, int] = { - name: index for index, name in enumerate(lineage_reference_map(references)) - } - - def marker(match: re.Match[str]) -> str: - position: int | None = positions.get(match.group(2)) - if position is None or match.group(1) == COMPACT_UNSTUBBED_REFERENCE_KIND: - return match.group(0) - return f"{COMPACT_RELATION_STUB_PREFIX}{position}" - - return COMPACT_REFERENCE_MARKER_PATTERN.sub(marker, query_sql) - - -def shareable_binding_prekeys(*, requests: Sequence[ModelSqlAnalysisRequest]) -> frozenset[str]: - """Return binding pre-keys shared by at least two binding requests of a compile.""" - - counts: Counter[str] = Counter( - binding_share_prekey(query_sql=request.query_sql, references=request.model_input.references) - for request in requests - if request.binding_schema is not None - ) - return frozenset( - prekey for prekey, count in counts.items() if count >= MIN_SHARED_BINDING_QUERY_MEMBERS - ) - - -def prepare_binding_queries( - *, - binding_catalog: Any, - inputs: CompactBatchInputs, - cleaned_sqls: list[str], - share: bool, - shareable_prekeys: frozenset[str] | None, -) -> tuple[PreparedBindingQuery | None, ...]: - """Prepare binding references in catalog order, with shared forms where keys can match.""" - - binding_schemas: tuple[dict[str, dict[str, str]] | None, ...] = inputs.binding_schemas or () - dialect: str | None = inputs.inference_profile.sql_analysis_dialect - prekeys: list[str | None] = [ - binding_share_prekey(query_sql=query_sql, references=query_references) - if share and binding_schema is not None - else None - for query_sql, query_references, binding_schema in zip( - inputs.query_sqls, inputs.references, binding_schemas, strict=True - ) - ] - candidates: frozenset[str] = ( - shareable_prekeys - if shareable_prekeys is not None - else frozenset( - prekey - for prekey, count in Counter(prekeys).items() - if prekey is not None and count >= MIN_SHARED_BINDING_QUERY_MEMBERS - ) - ) - shared_stubs: dict[int, dict[str, str]] = {} - for index, binding_schema in enumerate(binding_schemas): - prekey: str | None = prekeys[index] - if binding_schema is None or prekey is None or prekey not in candidates: - continue - names: list[str] | None = _shared_reference_names( - cleaned_sql=cleaned_sqls[index], - references=inputs.references[index], - binding_schema=binding_schema, - ) - if names is not None: - shared_stubs[index] = { - name: f"{COMPACT_RELATION_STUB_PREFIX}{position}" - for position, name in enumerate(names) - } - shared_sqls: dict[int, str | Exception] = dict( - zip( - shared_stubs, - normalize_analysis_sql_results( - dialect=dialect, - requests=[ - (inputs.query_sqls[index], stubs, inputs.placeholders[index]) - for index, stubs in shared_stubs.items() - ], - catalog=binding_catalog.native, - ), - strict=True, - ) - ) - prepared: list[PreparedBindingQuery | None] = [] - for index, binding_schema in enumerate(binding_schemas): - if binding_schema is None: - prepared.append(None) - continue - _, binding_references, overrides = binding_catalog.prepare( - [ - SqlSchemaValidationRequest( - sql=cleaned_sqls[index], dialect=dialect, schema=binding_schema - ) - ] - )[0] - shared: SharedBindingQuery | None = ( - _shared_binding_query( - binding_catalog=binding_catalog, - sql=normalized_analysis_sql(shared_sqls[index]), - dialect=dialect, - schema=binding_schema, - stubs=shared_stubs[index], - overrides=overrides, - ) - if index in shared_stubs - else None - ) - prepared.append( - PreparedBindingQuery(references=binding_references, overrides=overrides, shared=shared) - ) - if shareable_prekeys is not None: - return tuple(prepared) - counts: Counter[tuple[object, ...]] = Counter( - query.shared.key for query in prepared if query is not None and query.shared is not None - ) - return tuple( - ( - PreparedBindingQuery(references=query.references, overrides=query.overrides) - if query is not None - and query.shared is not None - and counts[query.shared.key] < MIN_SHARED_BINDING_QUERY_MEMBERS - else query - ) - for query in prepared - ) - - -def binding_query_fields( - *, - binding_query: PreparedBindingQuery, - binding_catalog: Any, -) -> tuple[dict[str, object], dict[str, object]]: - """Return native binding query fields and their identity payload.""" - - binding_references: list[tuple[str, bool]] = binding_query.references - fields: dict[str, object] = {} - shared: SharedBindingQuery | None = binding_query.shared - if shared is not None: - stubs: dict[str, str] = shared.stubs - binding_references = sorted( - binding_references, key=lambda reference: stubs.get(reference[0], reference[0]) - ) - fields["binding_aliases"] = { - name: stubs[name] for name, _ in binding_references if name in stubs - } - binding_payload: dict[str, object] = {"references": binding_references} - fields["binding_references"] = binding_references - if binding_query.overrides: - override_id: int = binding_catalog.native.register_override(binding_query.overrides) - fields["binding_override"] = override_id - binding_payload["override"] = override_id - return fields, binding_payload - - -def shared_result_is_exact(*, validation: object, template: object) -> bool: - """Only a successful analysis without diagnostics is independent of relation names.""" - - if isinstance(template, str): - return False - if validation is None: - return True - return isinstance(validation, dict) and cast(dict[str, object], validation).get("errors") == [] - - -def normalized_analysis_sql(result: str | Exception) -> str: - """Return one normalized batch member, raising the error its own normalization raised.""" - - if isinstance(result, Exception): - raise result - return result - - -def relation_stubs_collide(*, cleaned_sql: str, names: Iterable[str]) -> bool: - """Return whether authored SQL or relation names already use the relation stub prefix.""" - - return ( - any(name.startswith(COMPACT_RELATION_STUB_PREFIX) for name in names) - or COMPACT_RELATION_STUB_PREFIX.casefold() in cleaned_sql.casefold() - ) - - -def _shared_reference_names( - *, - cleaned_sql: str, - references: tuple[CompileSqlReference, ...], - binding_schema: Mapping[str, Mapping[str, str]], -) -> list[str] | None: - names: list[str] = list(lineage_reference_map(references)) - if not names or len({name.casefold() for name in names}) != len(names): - return None - if relation_stubs_collide(cleaned_sql=cleaned_sql, names=names): - return None - if SQL_QUOTED_IDENTIFIER_DELIMITER in cleaned_sql: - return None - for relation, columns in binding_schema.items(): - if ( - SQL_QUOTED_IDENTIFIER_DELIMITER in relation - or SQL_QUOTED_IDENTIFIER_DELIMITER in "".join(columns) - ): - return None - if _qualified_reference_names(query_sql=cleaned_sql, reference_names=names): - return None - return names - - -def _shared_binding_query( - *, - binding_catalog: Any, - sql: str, - dialect: str | None, - schema: Mapping[str, Mapping[str, str]], - stubs: dict[str, str], - overrides: Mapping[str, Mapping[str, str]], -) -> SharedBindingQuery: - """Identify one query from its SQL normalized with relation `stubs`.""" - - analysis_shapes: list[tuple[object, ...]] = [] - for name in stubs: - types, nullability = binding_catalog.analysis_shapes.get(name, ({}, {})) - analysis_shapes.append((stubs[name], tuple(types.items()), tuple(nullability.items()))) - binding_shapes: list[tuple[object, ...]] = [] - for relation, columns in schema.items(): - effective: Mapping[str, str] = ( - overrides[relation] - if relation in overrides - else binding_catalog.schemas[relation] - if columns - else {} - ) - binding_shapes.append( - (stubs.get(relation, relation), bool(columns), tuple(effective.items())) - ) - return SharedBindingQuery( - sql=sql, - stubs=stubs, - key=( - sql, - dialect or "generic", - tuple(analysis_shapes), - tuple(sorted(binding_shapes)), - ), - ) - - -def shared_result_keys( - *, inputs: CompactBatchInputs, context: CompactBatchContext -) -> tuple[tuple[object, ...] | None, ...]: - """Identify each shared member's complete native input, including its template.""" - - function_return_types: tuple[tuple[str, str], ...] = tuple( - inputs.inference_profile.function_return_types.items() - ) - keys: list[tuple[object, ...] | None] = [] - for index, binding_query in enumerate(context.binding_queries): - shared: SharedBindingQuery | None = ( - binding_query.shared if binding_query is not None else None - ) - if shared is None: - keys.append(None) - continue - query_references: tuple[CompileSqlReference, ...] = inputs.references[index] - declared_column_order: tuple[str, ...] = ( - tuple( - inputs.column_nullability_by_table.get( - _analysis_reference_name(query_references[0]), {} - ) - ) - if len(query_references) == 1 - else () - ) - reference_types: tuple[tuple[str, str], ...] = tuple( - (shared.stubs[name], resource_type.value) - for name, (resource_type, _) in lineage_reference_map(query_references).items() - ) - keys.append( - ( - shared.key, - inputs.recover_cte_facts[index], - inputs.rich_type_inference, - function_return_types, - declared_column_order, - reference_types, - ) - ) - return tuple(keys) - - -def reused_shared_results( - *, - inputs: CompactBatchInputs, - context: CompactBatchContext, - keys: tuple[tuple[object, ...] | None, ...], - memo: dict[object, object], -) -> dict[int, NativeCompactAnalysis]: - """Project remembered exact shared results onto members with the same key.""" - - reused: dict[int, NativeCompactAnalysis] = {} - for index, key in enumerate(keys): - representative: object = memo.get(key) if key is not None else None - binding_query: PreparedBindingQuery | None = context.binding_queries[index] - if not isinstance(representative, NativeCompactAnalysis) or binding_query is None: - continue - shared: SharedBindingQuery | None = binding_query.shared - if shared is None: - continue - resource_names_by_stub: dict[str, str] = { - shared.stubs[name]: resource_name - for name, (_, resource_name) in lineage_reference_map(inputs.references[index]).items() - } - resource_names: dict[int, str] = {} - for canonical_index in representative.resource_name_indexes: - resource_name: str | None = resource_names_by_stub.get( - representative.string_pool[canonical_index] - ) - if resource_name is None: - break - resource_names[canonical_index] = resource_name - else: - reused[index] = replace( - representative, - cleaned_sql=context.normalized_sqls[index], - resource_names=resource_names, - ) - return reused - - -def remembered_shared_results( - *, - keys: tuple[tuple[object, ...] | None, ...], - results: tuple[NativeCompactAnalysis, ...], -) -> dict[object, NativeCompactAnalysis]: - """Select exact, fully projected shared results to remember for later batches.""" - - remembered: dict[object, NativeCompactAnalysis] = {} - for key, result in zip(keys, results, strict=True): - if ( - key is not None - and key not in remembered - and result.projected - and result.analysis is not None - and result.compact_rows is not None - ): - remembered[key] = result - return remembered diff --git a/src/sqlbuild/compiler/compile/constants.py b/src/sqlbuild/compiler/compile/constants.py index 53a98bf891..1cd0e085bb 100644 --- a/src/sqlbuild/compiler/compile/constants.py +++ b/src/sqlbuild/compiler/compile/constants.py @@ -78,26 +78,14 @@ } ) -SQL_WILDCARD_TOKEN: str = "*" SQL_OPEN_PAREN_TOKEN: str = "(" SQL_ARGUMENT_SEPARATOR_TOKEN: str = "," SQL_STATEMENT_TERMINATOR_TOKEN: str = ";" -SQL_SINGLE_QUOTE_TOKEN: str = "'" SQL_QUOTE_TOKENS: frozenset[str] = frozenset({"'", '"', "`", "$"}) -SQL_QUALIFIER_SEPARATOR_TOKEN: str = "." SQL_WITH_KEYWORD: str = "WITH" SQL_CEREMONIAL_SELECT_VALUE: str = "1" OMITTED_CEREMONIAL_SELECT_SQL: str = f"\nSELECT {SQL_CEREMONIAL_SELECT_VALUE}" -UNKNOWN_SQL_TYPE_NAME: str = "UNKNOWN" -DECIMAL_SQL_TYPE_NAME: str = "DECIMAL" -RESOLVED_SOURCE_CONFIDENCE: str = "resolved" -POLYGLOT_SET_OPERATION_EXPRESSION_NAMES: frozenset[str] = frozenset( - {"Union", "Intersect", "Except"} -) -POLYGLOT_SELECT_EXPRESSION_NAME: str = "Select" -POLYGLOT_COLUMN_EXPRESSION_NAME: str = "Column" -POLYGLOT_WRAPPER_EXPRESSION_NAMES: frozenset[str] = frozenset({"Annotated", "Subquery", "Paren"}) TABLE_FUNCTION_RETURN_KEYS: frozenset[str] = frozenset({"table"}) @@ -132,28 +120,9 @@ TABLE_FN_ACTUAL_TEST_CTE_NAME: str = "__table_fn_actual__" TABLE_FN_EXPECTED_TEST_CTE_NAME: str = "__table_fn_expected__" DEFAULT_SQL_TEST_MODE: SqlTestMode = SqlTestMode.MODEL -RESERVED_SQL_TEST_CTE_NAMES: frozenset[str] = frozenset( - { - "__actual", - "__expected__typed", - "__actual__projected", - "__missing__", - "__unexpected__", - } -) COMPILE_CACHE_DISABLE_ENV_VAR: str = "SQLBUILD_DISABLE_COMPILE_CACHE" COMPILE_CACHE_DISABLE_VALUE: str = "1" COMPILE_INPUT_READS: CompileInputReadRegistry = CompileInputReadRegistry() -COMPACT_ANALYSIS_RESPONSE_LENGTH: int = 2 -COMPACT_RELATION_STUB_PREFIX: str = "__sqlbuild_project_input_" -COMPACT_REFERENCE_MARKER_PATTERN: re.Pattern[str] = re.compile( - r'__(ref|seed|source|dbt_ref)\((?:"[^"]+"\s*,\s*)?"([^"]+)"\)' -) -COMPACT_UNSTUBBED_REFERENCE_KIND: str = "dbt_ref" -MIN_SHARED_BINDING_QUERY_MEMBERS: int = 2 -COMPACT_ANALYSIS_LEGACY_RESPONSE_LENGTH: int = 3 -COMPACT_ANALYSIS_FACT_LENGTH: int = 6 -COMPACT_ANALYSIS_SOURCE_LENGTH: int = 3 SQL_TEST_SCAN_STORE_FILE_NAME: str = "sql-test-scans.bin" RETIRED_FACT_CACHE_DIRECTORY_NAME: str = "facts-v1" SQL_TEST_SCAN_STORE_VERSION: str = "sql-test-scan-store-v1" diff --git a/src/sqlbuild/compiler/sql_analysis/constants.py b/src/sqlbuild/compiler/sql_analysis/constants.py index ab2a88c690..240a115d0f 100644 --- a/src/sqlbuild/compiler/sql_analysis/constants.py +++ b/src/sqlbuild/compiler/sql_analysis/constants.py @@ -7,8 +7,6 @@ SQL_TEXT_START_CHARACTERS: frozenset[str] = frozenset({*SQL_QUOTE_CHARACTERS, "$"}) SQL_OPEN_PARENTHESIS: str = "(" SQL_CLOSE_PARENTHESIS: str = ")" -SQL_OPEN_BRACKET: str = "[" -SQL_CLOSE_BRACKET: str = "]" SQL_STRING_QUOTE_CHARACTER: str = "'" SQL_ESCAPE_CHARACTER: str = "\\" SQL_ESCAPE_STRING_PREFIX: str = "e" @@ -20,10 +18,8 @@ SUPPORTED_SQL_LINE_COMMENT_PREFIXES: frozenset[str] = frozenset({"--", "//", "#"}) SQL_BLOCK_COMMENT_OPEN: str = "/*" SQL_BLOCK_COMMENT_CLOSE: str = "*/" -SQL_DOLLAR_QUOTE_DELIMITER: str = "$$" SQL_DOLLAR_QUOTE_CHARACTER: str = "$" SQL_IDENTIFIER_PREFIX: str = "_" -SQL_NULL_KEYWORD: str = "NULL" SNOWFLAKE_DIALECT_NAME: str = "snowflake" POLYGLOT_ANALYZE_QUERY_ENTRY_POINT: str = "analyze_query" POLYGLOT_ANALYZE_QUERY_OPTIONS_ARGUMENT_COUNT: int = 2 @@ -36,142 +32,35 @@ POLYGLOT_SITE_RECORD_PREFIX: str = "polyglot-sites-" BINDING_UNKNOWN_TABLE_INTERNAL_CODE: str = "B000" -POLYGLOT_KIND_ALIAS: str = "alias" -POLYGLOT_KIND_AND: str = "and" POLYGLOT_KIND_ANNOTATED: str = "annotated" -POLYGLOT_KIND_ARRAY_AGG: str = "array_agg" -POLYGLOT_KIND_AVG: str = "avg" -POLYGLOT_KIND_BOOLEAN: str = "boolean" -POLYGLOT_KIND_CASE: str = "case" -POLYGLOT_KIND_CAST: str = "cast" -POLYGLOT_KIND_COALESCE: str = "coalesce" POLYGLOT_KIND_COLUMN: str = "column" -POLYGLOT_KIND_CONCAT: str = "concat" -POLYGLOT_KIND_COUNT: str = "count" POLYGLOT_KIND_EXCEPT: str = "except" -POLYGLOT_KIND_FUNCTION: str = "function" -POLYGLOT_KIND_IF_FUNC: str = "if_func" POLYGLOT_KIND_INTERSECT: str = "intersect" -POLYGLOT_KIND_IS_NULL: str = "is_null" -POLYGLOT_KIND_LITERAL: str = "literal" -POLYGLOT_KIND_MAX: str = "max" -POLYGLOT_KIND_MIN: str = "min" -POLYGLOT_KIND_PAREN: str = "paren" POLYGLOT_KIND_SELECT: str = "select" -POLYGLOT_KIND_STRING_AGG: str = "string_agg" -POLYGLOT_KIND_SUBSTRING: str = "substring" -POLYGLOT_KIND_SUM: str = "sum" POLYGLOT_KIND_TABLE: str = "table" -POLYGLOT_KIND_TIMESTAMP: str = "timestamp" -POLYGLOT_KIND_TRY_CAST: str = "try_cast" POLYGLOT_KIND_UNION: str = "union" -POLYGLOT_KIND_WINDOW_FUNCTION: str = "window_function" -POLYGLOT_KIND_WITHIN_GROUP: str = "within_group" -POLYGLOT_BOOLEAN_RESULT_KINDS: frozenset[str] = frozenset( - { - POLYGLOT_KIND_BOOLEAN, - "and", - "between", - "eq", - "exists", - "gt", - "gte", - "ilike", - "in", - "is", - POLYGLOT_KIND_IS_NULL, - "like", - "lt", - "lte", - "neq", - "not", - "or", - "regexp_like", - } -) POLYGLOT_SET_OPERATION_KINDS: frozenset[str] = frozenset( {POLYGLOT_KIND_UNION, POLYGLOT_KIND_INTERSECT, POLYGLOT_KIND_EXCEPT} ) -POLYGLOT_CAST_KINDS: frozenset[str] = frozenset({POLYGLOT_KIND_CAST, POLYGLOT_KIND_TRY_CAST}) -POLYGLOT_TYPE_PASSTHROUGH_KINDS: frozenset[str] = frozenset( - { - POLYGLOT_KIND_MAX, - POLYGLOT_KIND_MIN, - POLYGLOT_KIND_WINDOW_FUNCTION, - POLYGLOT_KIND_WITHIN_GROUP, - } -) -POLYGLOT_AGGREGATE_KINDS: frozenset[str] = frozenset( - { - POLYGLOT_KIND_AVG, - POLYGLOT_KIND_COUNT, - POLYGLOT_KIND_MAX, - POLYGLOT_KIND_MIN, - POLYGLOT_KIND_SUM, - POLYGLOT_KIND_ARRAY_AGG, - POLYGLOT_KIND_STRING_AGG, - } -) -POLYGLOT_PAYLOAD_ALIAS: str = "alias" POLYGLOT_PAYLOAD_COLUMN: str = "column" -POLYGLOT_PAYLOAD_DATA_TYPE: str = "data_type" -POLYGLOT_VARCHAR_DATA_TYPES: frozenset[str] = frozenset({"var_char", "varchar"}) -POLYGLOT_PAYLOAD_FROM: str = "from" -POLYGLOT_PAYLOAD_LEFT: str = "left" -POLYGLOT_PAYLOAD_EXPRESSIONS: str = "expressions" -POLYGLOT_PAYLOAD_JOINS: str = "joins" -POLYGLOT_PAYLOAD_KIND: str = "kind" -POLYGLOT_PAYLOAD_LITERAL_TYPE: str = "literal_type" POLYGLOT_PAYLOAD_NAME: str = "name" -POLYGLOT_PAYLOAD_NOT: str = "not" -POLYGLOT_PAYLOAD_PRECISION: str = "precision" -POLYGLOT_PAYLOAD_SCALE: str = "scale" -POLYGLOT_PAYLOAD_SELECT: str = "select" -POLYGLOT_PAYLOAD_RIGHT: str = "right" -POLYGLOT_PAYLOAD_TABLE: str = "table" -POLYGLOT_PAYLOAD_THIS: str = "this" -POLYGLOT_PAYLOAD_WHERE_CLAUSE: str = "where_clause" -POLYGLOT_PAYLOAD_TIMEZONE: str = "timezone" -POLYGLOT_PAYLOAD_TO: str = "to" -TIMESTAMP_WITH_TIME_ZONE_SQL_TYPE_NAME: str = "TIMESTAMPTZ" -POLYGLOT_JOIN_FULL: str = "FULL" -POLYGLOT_JOIN_LEFT: str = "LEFT" -POLYGLOT_JOIN_RIGHT: str = "RIGHT" -POLYGLOT_LITERAL_TYPE_STRING: str = "string" -POLYGLOT_NULLIF_FUNCTION_NAME: str = "NULLIF" -POLYGLOT_BINARY_OPERAND_COUNT: int = 2 -POLYGLOT_ANALYSIS_BASE_TABLES: str = "baseTables" -POLYGLOT_ANALYSIS_CAST_TYPE: str = "castType" -POLYGLOT_ANALYSIS_COLUMN_USES: str = "columnUses" -POLYGLOT_ANALYSIS_CONTEXT: str = "context" -POLYGLOT_ANALYSIS_CONTEXT_FILTER: str = "filter" -POLYGLOT_ANALYSIS_EXPRESSION_SQL: str = "expressionSql" POLYGLOT_ANALYSIS_IS_STAR: str = "isStar" POLYGLOT_ANALYSIS_NAME: str = "name" POLYGLOT_ANALYSIS_NULLABILITY: str = "nullability" POLYGLOT_ANALYSIS_NULLABILITY_NON_NULL: str = "non_null" POLYGLOT_ANALYSIS_NULLABILITY_NULLABLE: str = "nullable" POLYGLOT_ANALYSIS_PROJECTIONS: str = "projections" -POLYGLOT_ANALYSIS_RELATIONS: str = "relations" -POLYGLOT_ANALYSIS_SHAPE: str = "shape" POLYGLOT_ANALYSIS_SOURCE_ALIAS: str = "sourceAlias" POLYGLOT_ANALYSIS_SOURCE_CONFIDENCE: str = "confidence" POLYGLOT_ANALYSIS_SOURCE_NAME: str = "sourceName" POLYGLOT_ANALYSIS_TABLE: str = "table" POLYGLOT_ANALYSIS_TRANSFORM_KIND: str = "transformKind" -POLYGLOT_ANALYSIS_TRANSFORM_FUNCTION: str = "transformFunction" -POLYGLOT_ANALYSIS_FUNCTION_NAME: str = "name" POLYGLOT_ANALYSIS_STAR_PROJECTIONS: str = "starProjections" -POLYGLOT_ANALYSIS_TYPE_HINT: str = "typeHint" POLYGLOT_ANALYSIS_UPSTREAM: str = "upstream" -POLYGLOT_ANALYSIS_SHAPE_SELECT: str = "select" -POLYGLOT_ANALYSIS_SHAPE_SET_OPERATION: str = "set_operation" POLYGLOT_ANALYSIS_TRANSFORM_AGGREGATION: str = "aggregation" POLYGLOT_ANALYSIS_TRANSFORM_CAST: str = "cast" POLYGLOT_ANALYSIS_TRANSFORM_CONSTANT: str = "constant" POLYGLOT_ANALYSIS_TRANSFORM_DIRECT: str = "direct" -POLYGLOT_ANALYSIS_TRANSFORM_STAR: str = "star" -POLYGLOT_ANALYSIS_UNSAFE_TRANSFORMS: frozenset[str] = frozenset() TYPE_CHECKED_DIALECTS: frozenset[str] = frozenset({"duckdb", "snowflake", "postgresql", "bigquery"}) SQL_QUOTED_IDENTIFIER_DELIMITER: str = '"' CASE_SENSITIVE_BINDING_DIALECTS: frozenset[str] = frozenset({"snowflake", "postgresql"}) From 1d05892a12fae8eb5b80ac97ece1fd3a42610f81 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:07:07 +0100 Subject: [PATCH 26/36] feat(native): normalize parse and legacy query SQL inside native column inference Per-query column inference outside the session normalizes its parse and legacy SQL itself, in query order and with the unaliased dialect Python used, raising the same ValueError on rejection, so Python no longer round-trips each query through the normalizer first. --- .../_helpers/query_columns.rs | 24 +++++++++++- .../src/assembly/analysis_session/errors.rs | 8 ++++ .../analysis_session/main/query_columns.rs | 9 +++-- .../src/assembly/analysis_session/mod.rs | 1 + .../src/assembly/analysis_session/models.rs | 2 + .../analysis_session/query_columns.rs | 31 ++++++++++++--- src/sqlbuild/_native.pyi | 1 + .../_helpers/query_columns.py | 14 ++----- .../compiler/analysis_session/_test_types.py | 11 ++++++ .../test_native_query_columns.py | 39 +++++++++++++++++++ 10 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/errors.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs index 3acf6272a7..7a20a27572 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/query_columns.rs @@ -20,7 +20,29 @@ use crate::assembly::analysis_session::models::{ ModelReference, ModelRequest, QueryColumns, QueryColumnsInput, QueryColumnsMode, QueryColumnsRequest, }; -use crate::semantic_validation::models::ProjectCatalog; +use crate::semantic_validation::main::normalize::normalize_analysis_sql; +use crate::semantic_validation::models::{NormalizationInput, ProjectCatalog}; + +/// The request with `Parse` and `Legacy` SQL normalized in order; Err is the first rejection. +pub(crate) fn normalized_request( + request: &QueryColumnsRequest, +) -> Result { + let mut normalized: QueryColumnsRequest = request.clone(); + for query in &mut normalized.queries { + if matches!( + query.mode, + QueryColumnsMode::Parse | QueryColumnsMode::Legacy + ) { + query.sql = normalize_analysis_sql(NormalizationInput { + sql: std::mem::take(&mut query.sql), + dialect: request.normalization_dialect.clone(), + stubs: HashMap::new(), + placeholders: HashMap::new(), + })?; + } + } + Ok(normalized) +} /// Each query's columns, in order; only `Batch` queries read the catalog. pub(crate) fn query_columns( diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/errors.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/errors.rs new file mode 100644 index 0000000000..5fe6f12f33 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/errors.rs @@ -0,0 +1,8 @@ +//! Why per-query column analysis outside the session fails. + +/// A query Python's normalization rejects (its `ValueError`), or an internal native failure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryColumnsFailure { + Normalization(String), + Internal(String), +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs index f880ad0e58..33f14c2463 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/query_columns.rs @@ -1,13 +1,16 @@ //! Python's per-query column analysis outside the session. use crate::assembly::analysis_session::_helpers::query_columns as columns; +use crate::assembly::analysis_session::errors::QueryColumnsFailure; use crate::assembly::analysis_session::models::{QueryColumns, QueryColumnsRequest}; use crate::semantic_validation::models::ProjectCatalog; -/// Each query's columns, in order; only `Batch` queries read the catalog. +/// Each query's columns, in order; `Parse` and `Legacy` SQL is normalized first, in order. pub fn query_columns( catalog: Option<&ProjectCatalog>, request: &QueryColumnsRequest, -) -> Result, String> { - columns::query_columns(catalog, request) +) -> Result, QueryColumnsFailure> { + let request: QueryColumnsRequest = + columns::normalized_request(request).map_err(QueryColumnsFailure::Normalization)?; + columns::query_columns(catalog, &request).map_err(QueryColumnsFailure::Internal) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/mod.rs index 536cf2088d..283fdb1307 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod _helpers; pub(crate) mod constants; +pub mod errors; pub mod main; pub mod models; pub mod types; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index a0c8fc0593..ab8170f337 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -335,6 +335,8 @@ pub struct QueryColumnsInput { #[derive(Debug, Clone, Default)] pub struct QueryColumnsRequest { pub dialect: String, + /// The unaliased `profile.sql_analysis_dialect or "generic"` normalization reads. + pub normalization_dialect: String, pub function_return_types: Pairs, /// Adapter nullability rules as `(function name, rule id)`. pub nullability_rules: Option, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs index 65fbdf1cba..abd40f0195 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/query_columns.rs @@ -2,7 +2,8 @@ use pyo3::prelude::{Bound, Py, PyAny, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::types::PyDict; -use pyo3::{pyfunction, wrap_pyfunction}; +use pyo3::{PyErr, pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::assembly::analysis_session::errors::QueryColumnsFailure; use sqlbuild_analysis::assembly::analysis_session::main::projection_facts::projection_facts; use sqlbuild_analysis::assembly::analysis_session::main::query_columns::query_columns; use sqlbuild_analysis::assembly::analysis_session::models::{ @@ -31,8 +32,8 @@ type QueryRow = ( bool, String, ); -/// `(dialect, function return types, nullability rules, types, nullability, queries)`. -type RequestRow = (String, Pairs, Pairs, Shapes, Shapes, Vec); +/// `(dialect, return types, rules, types, nullability, queries, normalization dialect)`. +type RequestRow = (String, Pairs, Pairs, Shapes, Shapes, Vec, String); /// `([(name, type)], NULL literal names, quoted names)`. type ProjectionFactsRow = (Vec<(String, String)>, Vec, Vec); /// `(succeeded, [(name, type, nullability)] or None, has star)`. @@ -47,7 +48,15 @@ fn infer_query_columns( request: RequestRow, adapter_rules: Option<(Py, Py)>, ) -> PyResult> { - let (dialect, function_return_types, nullability_rules, types, nullability, queries) = request; + let ( + dialect, + function_return_types, + nullability_rules, + types, + nullability, + queries, + normalization_dialect, + ) = request; let queries: Vec = queries .into_iter() .map(query_input) @@ -56,6 +65,7 @@ fn infer_query_columns( let rule_failure: RuleFailure = RuleFailure::default(); let request = QueryColumnsRequest { dialect, + normalization_dialect, function_return_types, nullability_rules: Some(nullability_rules), nullability_callback: adapter_rules.map(|(rules, nullability)| { @@ -66,17 +76,28 @@ fn infer_query_columns( queries, }; let catalog = catalog.as_ref().map(|catalog| &catalog.inner); - let columns = py.compiler_detach(|| query_columns(catalog, &request)); + let columns = py.compiler_detach(|| Ok(query_columns(catalog, &request))); if let Some(error) = raised(&rule_failure) { return Err(error); } Ok(columns .map_err(|reason| compiler_error(native_failure(QUERY_COLUMNS_CONTEXT, &reason)))? + .map_err(query_columns_error)? .into_iter() .map(columns_row) .collect()) } +/// A normalization rejection as Python's `ValueError`; anything else as an internal failure. +fn query_columns_error(failure: QueryColumnsFailure) -> PyErr { + match failure { + QueryColumnsFailure::Normalization(message) => compiler_error(message), + QueryColumnsFailure::Internal(reason) => { + compiler_error(native_failure(QUERY_COLUMNS_CONTEXT, &reason)) + } + } +} + fn query_input(row: QueryRow) -> Result { let (sql, placeholders, analysis_names, lineage_references, recover_cte_facts, mode) = row; let mode: QueryColumnsMode = match mode.as_str() { diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 654b4b5cc0..2d59604fda 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -460,6 +460,7 @@ def infer_query_columns( list[tuple[str, list[tuple[str, str]]]], list[tuple[str, list[tuple[str, str]]]], list[tuple[object, ...]], + str, ], adapter_rules: tuple[Mapping[str, Callable[..., object]], type] | None = None, ) -> list[tuple[bool, list[tuple[str, str | None, str]] | None, bool]]: ... diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py index 3f05efbd63..9da526a761 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/query_columns.py @@ -25,10 +25,8 @@ TABLE_FUNCTION_ANALYSIS_PREFIX, ) from sqlbuild.compiler.sql_analysis.main._binding_catalog import create_binding_catalog -from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql _BATCH_MODE: str = "batch" -_NORMALIZED_MODES: frozenset[str] = frozenset({"parse", "legacy"}) _LINEAGE_RESOURCE_TYPES: dict[str, CompiledResourceType] = { SqlReferenceKind.REF: CompiledResourceType.MODEL, SqlReferenceKind.SOURCE: CompiledResourceType.SOURCE, @@ -76,10 +74,8 @@ def native_query_columns( nullability_rule_rows(profile), shape_rows(column_types_by_table), shape_rows(column_nullability_by_table), - [ - _query_row(query=query, dialect=profile.sql_analysis_dialect) - for query in queries - ], + [_query_row(query) for query in queries], + profile.sql_analysis_dialect or "generic", ), (adapter_rules, InferredNullability) if adapter_rules else None, ) @@ -103,12 +99,10 @@ def _inferred_columns( ) -def _query_row(*, query: NativeColumnQuery, dialect: str | None) -> tuple[object, ...]: +def _query_row(query: NativeColumnQuery) -> tuple[object, ...]: references: tuple[CompileSqlReference, ...] = query.references return ( - normalize_analysis_sql(sql=query.sql, dialect=dialect) - if query.mode in _NORMALIZED_MODES - else query.sql, + query.sql, list((query.placeholders or {}).items()), [_analysis_name(reference) for reference in references], [ diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index c15174ae86..7b53a6d733 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session.types import QueryColumnsMode from sqlbuild.compiler.lineage.types import ColumnLineageMode @@ -146,3 +147,13 @@ class ProjectionFactsTestCase: description: str queries: tuple[tuple[str, str], ...] expected_differences: list[tuple[int, str, object, object]] + + +@dataclass(frozen=True) +class NormalizationRejectionTestCase: + """Queries outside the session whose analysis SQL Python's normalization rejects.""" + + description: str + queries: tuple[tuple[str, QueryColumnsMode], ...] + dialect: str + expected_message: str diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py index f97308bc66..435209c048 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py @@ -6,17 +6,24 @@ from __future__ import annotations +import re from pathlib import Path import pytest import sqlbuild._native as native_module import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.analysis_session.main._infer_native_query_columns import ( + infer_native_query_columns, +) +from sqlbuild.compiler.analysis_session.models import NativeColumnQuery from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( FallbackColumnsTestCase, MissingSessionAnalysisTestCase, + NormalizationRejectionTestCase, TableFunctionColumnsTestCase, ) from tests.integration.src.sqlbuild.compiler.lineage.helpers import compiled_project @@ -138,5 +145,37 @@ def test_given_session_without_model_analysis_when_assembling_then_raises_native assert str(raised.value) == test_case.expected_message +@pytest.mark.parametrize( + "test_case", + [ + NormalizationRejectionTestCase( + description="a parse query whose table-function call never closes", + queries=(("SELECT 1 AS a", "parse"), ('SELECT * FROM __table_fn("f")(1', "parse")), + dialect="duckdb", + expected_message="invalid table-function analysis input: Parenthesis", + ), + NormalizationRejectionTestCase( + description="a legacy query whose table-function argument quote never closes", + queries=(('SELECT * FROM __table_fn("f")(\'x)', "legacy"),), + dialect="motherduck", + expected_message="invalid table-function analysis input: Quote", + ), + ], + ids=lambda case: case.description, +) +def test_given_sql_normalization_rejects_when_inferring_columns_then_python_value_error_raises( + test_case: NormalizationRejectionTestCase, +) -> None: + queries: tuple[NativeColumnQuery, ...] = tuple( + NativeColumnQuery(sql=sql, mode=mode) for sql, mode in test_case.queries + ) + + with pytest.raises(ValueError, match=re.escape(test_case.expected_message)): + _ = infer_native_query_columns( + queries=queries, + profile=ExpressionInferenceProfile(sql_analysis_dialect=test_case.dialect), + ) + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From 4bdc0cc88e1114a6f6a15458e3ab2e3b89880e4f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:18:06 +0100 Subject: [PATCH 27/36] feat(native): collect SQL-test declaration usages from the native reference scan The usage walk for macro SQL tests now reads the native @enum/@const scan in a mode that resumes past malformed references, as Python's walk did, and raises the same unclosed quote or comment errors. Python's reference start walk and its patterns are deleted. A golden recorded from Python's walk covers malformed, quoted, commented and unclosed text. --- .../sqltext/declaration_references.rs | 23 ++++- .../declaration_references/scanner.rs | 32 ++++++- .../compiler/main/declaration_references.rs | 5 +- .../src/compiler/tests/helpers.rs | 2 +- src/sqlbuild/_native.pyi | 3 + .../compile/_helpers/render/declarations.py | 87 +++++-------------- .../_scan_native_declaration_references.py | 15 +++- .../declaration_usages_usage_scan_edges.json | 86 ++++++++++++++++++ .../sqlbuild/compiler/compile/_test_types.py | 9 ++ .../src/sqlbuild/compiler/compile/helpers.py | 46 +++++++++- .../compile/test_native_declaration_usages.py | 68 +++++++++++++++ 11 files changed, 296 insertions(+), 80 deletions(-) create mode 100644 tests/goldens/python_oracle/declaration_usages_usage_scan_edges.json create mode 100644 tests/integration/src/sqlbuild/compiler/compile/test_native_declaration_usages.py diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/declaration_references.rs b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/declaration_references.rs index 384070befe..b01a3a7760 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/declaration_references.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/declaration_references.rs @@ -25,12 +25,32 @@ fn scan_sql_declaration_references( sqls: Vec, python_version: (u8, u8), unicode_version: &str, +) -> PyResult> { + scanned_rows(py, sqls, (python_version, unicode_version), false) +} + +/// The same scan walking past malformed references, as Python's declaration usage walk did. +#[pyfunction] +fn scan_sql_declaration_usages( + py: Python<'_>, + sqls: Vec, + python_version: (u8, u8), + unicode_version: &str, +) -> PyResult> { + scanned_rows(py, sqls, (python_version, unicode_version), true) +} + +fn scanned_rows( + py: Python<'_>, + sqls: Vec, + (python_version, unicode_version): ((u8, u8), &str), + skip_malformed: bool, ) -> PyResult> { let python = python_text(python_version, unicode_version).ok_or_else(|| { PyRuntimeError::new_err("declaration scanning needs a supported Python (see D017)") })?; let scanned: Vec = py - .compiler_detach(|| Ok(scan_declaration_references(python, &sqls))) + .compiler_detach(|| Ok(scan_declaration_references(python, &sqls, skip_malformed))) .map_err(compiler_error)?; Ok(scanned.into_iter().map(scan_row).collect()) } @@ -63,5 +83,6 @@ fn reference_row(reference: DeclarationReference) -> ReferenceRow { pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(scan_sql_declaration_references, module)?)?; + module.add_function(wrap_pyfunction!(scan_sql_declaration_usages, module)?)?; Ok(()) } diff --git a/crates/sqlbuild-sqltext/src/compiler/_helpers/declaration_references/scanner.rs b/crates/sqlbuild-sqltext/src/compiler/_helpers/declaration_references/scanner.rs index 60c0747554..0e9ddbbbe3 100644 --- a/crates/sqlbuild-sqltext/src/compiler/_helpers/declaration_references/scanner.rs +++ b/crates/sqlbuild-sqltext/src/compiler/_helpers/declaration_references/scanner.rs @@ -4,21 +4,30 @@ use crate::compiler::_helpers::declaration_references::reference_syntax::{ MatchedReference, ReferenceSyntax, match_reference, }; use crate::compiler::models::{ - DeclarationReference, DeclarationReferenceScan, DeclarationReferenceStop, + DeclarationReference, DeclarationReferenceKind, DeclarationReferenceScan, + DeclarationReferenceStop, }; use sqlbuild_core::text::models::PythonText; const SPECIAL_BYTES: &[u8] = b"'\"`$@/-"; +const ENUM_KEYWORD: &str = "@enum"; +const CONSTANT_KEYWORD: &str = "@const"; /// Where the walk for the next reference start ended. enum NextStart<'sql> { Reference(usize, MatchedReference<'sql>), + /// A malformed reference start and the byte offset after its `@enum`/`@const` keyword. + Malformed(DeclarationReferenceKind, usize), End, Stop(DeclarationReferenceStop), } -/// Python's references in `sql` (code points) and its stopping error. -pub(crate) fn scan_references(python: PythonText, sql: &str) -> DeclarationReferenceScan { +/// Python's references in `sql` (code points) and stop; `skip_malformed` resumes past malformed. +pub(crate) fn scan_references( + python: PythonText, + sql: &str, + skip_malformed: bool, +) -> DeclarationReferenceScan { let mut references: Vec = Vec::new(); let mut offsets: CharOffsets = CharOffsets::default(); let mut cursor: usize = 0; @@ -27,6 +36,14 @@ pub(crate) fn scan_references(python: PythonText, sql: &str) -> DeclarationRefer let (start, matched) = match next_reference_start(python, sql, cursor) { NextStart::Reference(start, matched) => (start, matched), NextStart::End => break, + NextStart::Malformed(_, keyword_end) if skip_malformed => { + cursor = keyword_end; + continue; + } + NextStart::Malformed(kind, _) => { + stop = Some(DeclarationReferenceStop::Malformed(kind)); + break; + } NextStart::Stop(found) => { stop = Some(found); break; @@ -77,7 +94,7 @@ fn next_reference_start(python: PythonText, sql: &str, start: usize) -> NextStar ReferenceSyntax::NotReference => Some(index + 1), ReferenceSyntax::Matched(matched) => return NextStart::Reference(index, matched), ReferenceSyntax::Malformed(kind) => { - return NextStart::Stop(DeclarationReferenceStop::Malformed(kind)); + return NextStart::Malformed(kind, index + keyword_length(kind)); } }, _ => Some(index + 1), @@ -90,6 +107,13 @@ fn next_reference_start(python: PythonText, sql: &str, start: usize) -> NextStar NextStart::End } +fn keyword_length(kind: DeclarationReferenceKind) -> usize { + match kind { + DeclarationReferenceKind::Enum => ENUM_KEYWORD.len(), + DeclarationReferenceKind::Constant => CONSTANT_KEYWORD.len(), + } +} + /// Python's `quoted_text_end_impl` for `'`, `"` (doubled quotes escape) and backticks. fn quoted_end(bytes: &[u8], start: usize, doubled_escapes: bool) -> Option { let quote: u8 = bytes[start]; diff --git a/crates/sqlbuild-sqltext/src/compiler/main/declaration_references.rs b/crates/sqlbuild-sqltext/src/compiler/main/declaration_references.rs index 5e5fd1ec90..0a35f3943a 100644 --- a/crates/sqlbuild-sqltext/src/compiler/main/declaration_references.rs +++ b/crates/sqlbuild-sqltext/src/compiler/main/declaration_references.rs @@ -6,13 +6,14 @@ use crate::compiler::_helpers::declaration_references::scanner::scan_references; use crate::compiler::models::DeclarationReferenceScan; use sqlbuild_core::text::models::PythonText; -/// Each SQL string's references and stopping error, with `python`'s word boundaries. +/// Each SQL string's references and stopping error; `skip_malformed` walks past malformed starts. #[must_use] pub fn scan_declaration_references( python: PythonText, sqls: &[String], + skip_malformed: bool, ) -> Vec { sqls.par_iter() - .map(|sql| scan_references(python, sql)) + .map(|sql| scan_references(python, sql, skip_malformed)) .collect() } diff --git a/crates/sqlbuild-sqltext/src/compiler/tests/helpers.rs b/crates/sqlbuild-sqltext/src/compiler/tests/helpers.rs index 8822423b6e..bc850546cf 100644 --- a/crates/sqlbuild-sqltext/src/compiler/tests/helpers.rs +++ b/crates/sqlbuild-sqltext/src/compiler/tests/helpers.rs @@ -142,7 +142,7 @@ pub(crate) fn batch_sizes_bound_workers_by_contract() -> bool { pub(crate) fn scanned_references(sql: &str) -> Option { let python = python_text((3, 12), "15.0.0").expect("Python 3.12 is supported"); let scan: DeclarationReferenceScan = - scan_declaration_references(python, &[sql.to_owned()]).pop()?; + scan_declaration_references(python, &[sql.to_owned()], false).pop()?; let references: String = scan .references .iter() diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 2d59604fda..e529f56d29 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -310,6 +310,9 @@ def parse_macro_arguments(text: str, nested: list[tuple[int, int]]) -> tuple[obj def scan_sql_declaration_references( sqls: list[str], python_version: tuple[int, int], unicode_version: str ) -> list[tuple[list[tuple[int, str, str | None, int, int]], int | None]]: ... +def scan_sql_declaration_usages( + sqls: list[str], python_version: tuple[int, int], unicode_version: str +) -> list[tuple[list[tuple[int, str, str | None, int, int]], int | None]]: ... def interpolate_sql_batch( sqls: list[tuple[str, str]], sources: tuple[ diff --git a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py index 1106e3bf14..25c57555fe 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py @@ -2,14 +2,12 @@ from __future__ import annotations -import re from collections.abc import Mapping from dataclasses import replace from pathlib import Path from types import MappingProxyType from typing import cast -from sqlbuild.compiler.compile.constants import MACRO_TOKEN, SQL_QUOTE_TOKENS from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( DeclarationExpansionContext, @@ -43,6 +41,9 @@ from sqlbuild.compiler.model_loop.main._build_native_declaration_contexts import ( build_native_declaration_contexts, ) +from sqlbuild.compiler.model_loop.main._scan_native_declaration_references import ( + scan_native_declaration_references, +) from sqlbuild.compiler.model_loop.types import NativeDeclarationScan from sqlbuild.compiler.planner.types import ContractPolicy from sqlbuild.compiler.scopes.constants import CURRENT_PATH_COMPONENT, QUALIFIED_IDENTITY_SEPARATOR @@ -69,24 +70,11 @@ ScopeKind, UsageKind, ) -from sqlbuild.compiler.sql_analysis.main._skip_block_comment import skip_block_comment -from sqlbuild.compiler.sql_analysis.main._skip_line_comment import skip_line_comment -from sqlbuild.compiler.sql_analysis.main._skip_quoted_text import skip_quoted_text from sqlbuild.spec.contracts.models import SchemaAuditInstance, SchemaColumn, SchemaModelEntry from sqlbuild.sql_values.exceptions import SqlValueRenderingError, SqlValueValidationError from sqlbuild.sql_values.main.validate_rendered_size import validate_rendered_sql_value_size from sqlbuild.sql_values.types import CollectionRendering, SqlValueKind -_ENUM_REFERENCE_PATTERN: re.Pattern[str] = re.compile( - r"@enum\s*\(\s*(?P['\"])(?P[A-Za-z_][A-Za-z0-9_]*)" - r"(?P=quote)\s*\)\s*\.\s*(?P[A-Za-z_][A-Za-z0-9_]*)" -) -_CONSTANT_REFERENCE_PATTERN: re.Pattern[str] = re.compile( - r"@const\s*\(\s*(?P['\"])(?P[A-Za-z_][A-Za-z0-9_]*)" - r"(?P=quote)\s*\)" -) -_DECLARATION_REFERENCE_START_PATTERN: re.Pattern[str] = re.compile(r"@(?Penum|const)\b") -_DECLARATION_SCAN_SPECIAL: re.Pattern[str] = re.compile(r"['\"`$@/-]") _CONTEXT: str = "Enum and constant expansion" _ACCEPTED_VALUES_AUDIT: str = "accepted_values" _ENUM_REFERENCE_KIND: str = "enum" @@ -312,39 +300,25 @@ def declaration_usage_records( ) -> tuple[UsageRecord, ...]: """Collect declaration usages with path or expected-model provenance.""" + scanned, stop = scan_native_declaration_references(sqls=(sql,), skip_malformed=True)[0] + if stop is not None: + raise CompileInputError(_unclosed_text_message(stop)) usages: list[UsageRecord] = [] - cursor: int = 0 - while (reference_start := _find_next_reference_start(sql=sql, start=cursor)) is not None: - start_match: re.Match[str] | None = _DECLARATION_REFERENCE_START_PATTERN.match( - sql, reference_start - ) - if start_match is None: - break - is_enum: bool = start_match.group("kind") == _ENUM_REFERENCE_KIND - match: re.Match[str] | None = ( - _ENUM_REFERENCE_PATTERN.match(sql, reference_start) - if is_enum - else _CONSTANT_REFERENCE_PATTERN.match(sql, reference_start) - ) - if match is None: - cursor = start_match.end() - continue - name: str = match.group("name") + for kind, name, _member, _start, _end in scanned: visibility: tuple[VisibilityRecord, ...] = ( declarations.enum_visibility.get(name, ()) - if is_enum + if kind == ENUM_REFERENCE_KIND_CODE else declarations.constant_visibility.get(name, ()) ) - for record in usage_visibility(visibility=visibility, consumer=resource): - usages.append( - UsageRecord( - consumer=resource, - declaration=record.declaration, - kind=UsageKind.RUNTIME, - through=record.through, - ) + usages.extend( + UsageRecord( + consumer=resource, + declaration=record.declaration, + kind=UsageKind.RUNTIME, + through=record.through, ) - cursor = match.end() + for record in usage_visibility(visibility=visibility, consumer=resource) + ) return tuple(dict.fromkeys(usages)) @@ -570,6 +544,10 @@ def _stop_message(*, stop: int, file_path: Path) -> str: return _invalid_reference_message(kind=_ENUM_REFERENCE_KIND, file_path=file_path) if stop == INVALID_CONSTANT_REFERENCE_STOP_CODE: return _invalid_reference_message(kind=_CONSTANT_REFERENCE_KIND, file_path=file_path) + return _unclosed_text_message(stop) + + +def _unclosed_text_message(stop: int) -> str: if stop == UNCLOSED_BLOCK_COMMENT_STOP_CODE: return f"{_CONTEXT} contains an unclosed block comment" return f"{_CONTEXT} contains an unclosed quoted string" @@ -770,28 +748,3 @@ def render_enum_member_value(*, value: str | int) -> str: return str(value) escaped_value: str = value.replace("'", "''") return f"'{escaped_value}'" - - -def _find_next_reference_start(*, sql: str, start: int) -> int | None: - if sql.find("@enum", start) < 0 and sql.find("@const", start) < 0: - return None - index: int = start - while index < len(sql): - special: re.Match[str] | None = _DECLARATION_SCAN_SPECIAL.search(sql, index) - if special is None: - return None - index = special.start() - character: str = sql[index] - if character in SQL_QUOTE_TOKENS: - index = skip_quoted_text(sql=sql, start=index, context=_CONTEXT) - continue - if sql.startswith("--", index): - index = skip_line_comment(sql=sql, start=index) - continue - if sql.startswith("/*", index): - index = skip_block_comment(sql=sql, start=index, context=_CONTEXT) - continue - if character == MACRO_TOKEN and _DECLARATION_REFERENCE_START_PATTERN.match(sql, index): - return index - index += 1 - return None diff --git a/src/sqlbuild/compiler/model_loop/main/_scan_native_declaration_references.py b/src/sqlbuild/compiler/model_loop/main/_scan_native_declaration_references.py index 744faabeb5..269354d7ca 100644 --- a/src/sqlbuild/compiler/model_loop/main/_scan_native_declaration_references.py +++ b/src/sqlbuild/compiler/model_loop/main/_scan_native_declaration_references.py @@ -4,19 +4,28 @@ import sys import unicodedata +from collections.abc import Callable import sqlbuild._native as _native from sqlbuild.compiler.model_loop.types import NativeDeclarationScan def scan_native_declaration_references( - *, sqls: tuple[str, ...] + *, sqls: tuple[str, ...], skip_malformed: bool = False ) -> tuple[NativeDeclarationScan, ...]: - """Return each string's references and the code of the error ending its walk.""" + """Return each string's references and stop code; `skip_malformed` walks past malformed ones.""" + scan_references: Callable[ + [list[str], tuple[int, int], str], + list[tuple[list[tuple[int, str, str | None, int, int]], int | None]], + ] = ( + _native.scan_sql_declaration_usages + if skip_malformed + else _native.scan_sql_declaration_references + ) return tuple( (tuple(scan[0]), scan[1]) - for scan in _native.scan_sql_declaration_references( + for scan in scan_references( list(sqls), (sys.version_info[0], sys.version_info[1]), unicodedata.unidata_version ) ) diff --git a/tests/goldens/python_oracle/declaration_usages_usage_scan_edges.json b/tests/goldens/python_oracle/declaration_usages_usage_scan_edges.json new file mode 100644 index 0000000000..431e8234f4 --- /dev/null +++ b/tests/goldens/python_oracle/declaration_usages_usage_scan_edges.json @@ -0,0 +1,86 @@ +[ +[ +"declaration_usages", +"6adc2535c49443cb6b3cc3cd" +], +[ +"declaration_usages", +"6070cec0a6fcc43f8c7ab528" +], +[ +"declaration_usages", +"6786f18f6ff42aa577c99d86" +], +[ +"declaration_usages", +"6070cec0a6fcc43f8c7ab528" +], +[ +"declaration_usages", +"7851fd987ffcb6afe0244d4d" +], +[ +"declaration_usages", +"6070cec0a6fcc43f8c7ab528" +], +[ +"declaration_usages", +"6070cec0a6fcc43f8c7ab528" +], +[ +"declaration_usages", +"7851fd987ffcb6afe0244d4d" +], +[ +"declaration_usages", +"6070cec0a6fcc43f8c7ab528" +], +[ +"declaration_usages", +"99c78fa41fe002d8642754e5" +], +[ +"declaration_usages", +"6786f18f6ff42aa577c99d86" +], +[ +"declaration_usages", +"ac6c4233f9df675ec4b5f9fd" +], +[ +"declaration_usages", +"6786f18f6ff42aa577c99d86" +], +[ +"declaration_usages", +"6786f18f6ff42aa577c99d86" +], +[ +"declaration_usages", +"6adc2535c49443cb6b3cc3cd" +], +[ +"declaration_usages", +"6786f18f6ff42aa577c99d86" +], +[ +"declaration_usages", +"077a3f290320a2b8cea483fc" +], +[ +"declaration_usages", +"6f95d4337ea1069397695fc8" +], +[ +"declaration_usages", +"6f95d4337ea1069397695fc8" +], +[ +"declaration_usages", +"6f95d4337ea1069397695fc8" +], +[ +"declaration_usages", +"077a3f290320a2b8cea483fc" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py index bddab3ba58..f75f91d1ab 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/compile/_test_types.py @@ -64,3 +64,12 @@ class FailingMacroProjectParityTestCase: expected_minimum_errors_after_macro_runs: int expected_minimum_distinct_errors: int expected_mismatches: list[tuple[object, object, object]] + + +@dataclass(frozen=True) +class DeclarationUsageTestCase: + """SQL texts whose declaration usages Python's usage walk recorded in a golden.""" + + description: str + texts: tuple[str, ...] + expected_differences: list[tuple[int, str, object, object]] diff --git a/tests/integration/src/sqlbuild/compiler/compile/helpers.py b/tests/integration/src/sqlbuild/compiler/compile/helpers.py index 8378aafded..1a104bc0fa 100644 --- a/tests/integration/src/sqlbuild/compiler/compile/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/compile/helpers.py @@ -15,12 +15,14 @@ import pytest from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter +from sqlbuild.compiler.compile._helpers.render.declarations import declaration_usage_records from sqlbuild.compiler.compile._helpers.render.macros import expand_sql_macros_result from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( CompileAdapterContext, CompileProjectInputs, + DeclarationResolutionContext, LoadedMacro, MacroContext, MacroExpansionResult, @@ -37,8 +39,8 @@ from sqlbuild.compiler.frontier.types import CompilerStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE -from sqlbuild.compiler.scopes.models import ResourceIdentity -from sqlbuild.compiler.scopes.types import ResourceKind +from sqlbuild.compiler.scopes.models import DeclarationIdentity, ResourceIdentity, VisibilityRecord +from sqlbuild.compiler.scopes.types import DeclarationKind, ResourceKind, VisibilityReason from sqlbuild.spec.contracts.main.resolve_effective_collection_rendering import ( resolve_effective_collection_rendering, ) @@ -556,3 +558,43 @@ def engine_render_outcome( ), }[failure is None] return outcome, tuple(log_path.read_text(encoding="utf-8").splitlines()) + + +_USAGE_CONSUMER: ResourceIdentity = ResourceIdentity(ResourceKind.TEST, "orders_test") + + +def _visible(kind: DeclarationKind, name: str) -> tuple[VisibilityRecord, ...]: + return ( + VisibilityRecord( + resource=_USAGE_CONSUMER, + declaration=DeclarationIdentity(kind, name), + reason=VisibilityReason.GLOBAL, + ), + ) + + +_USAGE_DECLARATIONS: DeclarationResolutionContext = DeclarationResolutionContext( + enum_visibility={ + "status": _visible(DeclarationKind.ENUM, "status"), + "size": _visible(DeclarationKind.ENUM, "size"), + }, + constant_visibility={"rate": _visible(DeclarationKind.CONSTANT, "rate")}, +) + + +def declaration_usage_outcome(sql: str) -> tuple[object, ...]: + """The names of the declarations `sql` uses, in order, or the error the scan raises.""" + + capture: ExpansionFailureCapture = ExpansionFailureCapture() + names: list[tuple[object, ...]] = [()] + with capture: + names[0] = tuple( + usage.declaration.name + for usage in declaration_usage_records( + sql=sql, resource=_USAGE_CONSUMER, declarations=_USAGE_DECLARATIONS + ) + ) + return { + True: names[0], + False: (type(capture.failure).__name__, str(capture.failure)), + }[capture.failure is None] diff --git a/tests/integration/src/sqlbuild/compiler/compile/test_native_declaration_usages.py b/tests/integration/src/sqlbuild/compiler/compile/test_native_declaration_usages.py new file mode 100644 index 0000000000..7f84016974 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/compile/test_native_declaration_usages.py @@ -0,0 +1,68 @@ +"""SQL-test declaration usages come from the native reference scan and match Python's walk.""" + +from __future__ import annotations + +import pytest + +from tests.integration.src.sqlbuild.compiler.compile._test_types import DeclarationUsageTestCase +from tests.integration.src.sqlbuild.compiler.compile.helpers import declaration_usage_outcome +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) + + +@pytest.mark.parametrize( + "test_case", + [ + DeclarationUsageTestCase( + description="usage scan edges", + texts=( + "SELECT @enum('status').OPEN, @const('rate')", + "SELECT @enum('status') , @const('rate')", + "SELECT @enum x 'unclosed", + "SELECT @enum x, @const('rate') 'unclosed", + 'SELECT @const(rate), @enum("size").S', + "SELECT '@enum(''status'').A', @const('rate') /* open", + "SELECT @enumx('status').A, @const_x('rate'), @consté, @const('rate')", + "SELECT $$ @enum('status').A $$, @enum('size').M -- @const('rate')\n", + "SELECT x$$ @const('rate') $$", + "SELECT @enum ( 'status' ) . OPEN, @enum('status').OPEN, @const('missing')", + "SELECT `@const('rate')`, \"@enum('size').A\"", + "SELECT @const('rate'), @const('rate'), 'é' @enum('size').XL", + "SELECT @@const, @enum, @const", + "SELECT 1", + "SELECT @enum('status').OPEN /* @const('rate') */ 'it''s' @const('rate')", + "SELECT @const('rate' ", + "SELECT /* x @const('rate')", + "SELECT 'a @enum('status').A", + "SELECT @enum x 'oops @const('rate')", + "SELECT @const('rate') $tag$ @enum('size').S", + "SELECT @enum(\"status\").OPEN, @const('rate') /* tail @enum('size').S", + ), + expected_differences=[], + ), + ], + ids=lambda case: case.description, +) +def test_given_declaration_references_when_collecting_usages_then_matches_python_golden( + test_case: DeclarationUsageTestCase, +) -> None: + outcomes: list[tuple[object, ...]] = [declaration_usage_outcome(sql) for sql in test_case.texts] + expected: list[GoldenEntry] = read_golden( + golden_name("declaration_usages", test_case.description) + ) + + assert ( + golden_differences( + expected, [golden_entry("declaration_usages", outcome) for outcome in outcomes] + ) + == test_case.expected_differences + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) From 952211642a50854b3f1c0a1d1be5fea0117ac187 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:27:21 +0100 Subject: [PATCH 28/36] feat(native): extract hard-coded relation names natively Relation names in Python-node SQL and the runtime relation guard come from the native parse and a port of Python's dict walk (table payloads, CTE names and temporary tables per statement, first-seen order). An unknown dialect still raises the wheel's ValueError and unparseable SQL still yields None. A golden from Python's walk covers qualifiers, CTE shadowing, temporary tables, DML, quoting and non-ASCII names; the site recorder test now records the rule document's wheel parse. --- crates/sqlbuild-analysis/src/lib.rs | 1 + .../src/relation_names/_helpers/mod.rs | 1 + .../src/relation_names/_helpers/walk.rs | 141 ++++++++++++++++++ .../src/relation_names/constants.rs | 15 ++ .../src/relation_names/errors.rs | 8 + .../main/extract_relation_names.rs | 13 ++ .../src/relation_names/main/mod.rs | 1 + .../src/relation_names/mod.rs | 7 + .../src/relation_names/models.rs | 16 ++ .../src/bindings/_helpers/analysis/mod.rs | 1 + .../src/bindings/_helpers/analysis/queries.rs | 1 + .../_helpers/analysis/relation_names.rs | 55 +++++++ src/sqlbuild/_native.pyi | 3 + .../references/_helpers/relation_names.py | 111 ++------------ ...ation_names_hard_coded_relation_edges.json | 90 +++++++++++ .../compiler/sql_analysis/_test_types.py | 9 ++ .../sqlbuild/compiler/sql_analysis/helpers.py | 38 +++++ .../test_native_relation_names.py | 87 +++++++++++ .../test_polyglot_site_recorder.py | 9 +- 19 files changed, 504 insertions(+), 103 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/relation_names/_helpers/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/_helpers/walk.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/constants.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/errors.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/main/extract_relation_names.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/main/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_names/models.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/analysis/relation_names.rs create mode 100644 tests/goldens/python_oracle/relation_names_hard_coded_relation_edges.json create mode 100644 tests/integration/src/sqlbuild/compiler/sql_analysis/test_native_relation_names.py diff --git a/crates/sqlbuild-analysis/src/lib.rs b/crates/sqlbuild-analysis/src/lib.rs index 2c6aee23ac..29ec9b3310 100644 --- a/crates/sqlbuild-analysis/src/lib.rs +++ b/crates/sqlbuild-analysis/src/lib.rs @@ -9,6 +9,7 @@ mod constants; pub mod contracts; pub mod lineage; pub mod query_analysis; +pub mod relation_names; pub mod semantic_checks; pub mod semantic_usage; pub mod semantic_validation; diff --git a/crates/sqlbuild-analysis/src/relation_names/_helpers/mod.rs b/crates/sqlbuild-analysis/src/relation_names/_helpers/mod.rs new file mode 100644 index 0000000000..208de6bb67 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/_helpers/mod.rs @@ -0,0 +1 @@ +pub(crate) mod walk; diff --git a/crates/sqlbuild-analysis/src/relation_names/_helpers/walk.rs b/crates/sqlbuild-analysis/src/relation_names/_helpers/walk.rs new file mode 100644 index 0000000000..e9cea286ab --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/_helpers/walk.rs @@ -0,0 +1,141 @@ +//! Python's dict walk over each parsed statement: tables, CTE names and temporary tables. + +use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions}; +use serde_json::{Map, Value, json}; +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::models::PythonText; + +use crate::relation_names::constants::{ + CATALOG_KEY, CREATE_TABLE_KEY, CTE_ALIAS_KEY, CTE_LIST_KEY, GENERIC_DIALECT, + MAX_FUNCTION_CALL_DEPTH, NAME_KEY, QUALIFIER_SEPARATOR, SCHEMA_KEY, TABLE_KEYS, TEMPORARY_KEY, +}; +use crate::relation_names::errors::RelationNamesFailure; +use crate::relation_names::models::{ExtractedRelations, RelationName}; + +/// Python's `extract_relation_names_impl`: None where the wheel's `parse` raises `PolyglotError`. +pub(crate) fn extracted_relations( + sql: &str, + dialect: Option<&str>, +) -> Result, RelationNamesFailure> { + let dialect: &str = dialect + .filter(|name| !name.is_empty()) + .unwrap_or(GENERIC_DIALECT); + let Some(statements) = parsed_statements(sql, dialect)? else { + return Ok(None); + }; + let python: PythonText = active_python_text(); + let mut extracted: ExtractedRelations = ExtractedRelations::default(); + for statement in &statements { + let tree: Value = serde_json::to_value(statement) + .map_err(|error| RelationNamesFailure::Internal(error.to_string()))?; + let nodes: Vec<&Map> = walked(&tree); + let cte_names: Vec = nodes + .iter() + .flat_map(|node| cte_names(python, node)) + .collect(); + extracted + .temporary + .extend(nodes.iter().filter_map(|node| temporary_name(python, node))); + let relations = nodes.iter().filter_map(|node| table_name(node)); + for relation in relations { + let unqualified: bool = relation.schema.is_none() && relation.database.is_none(); + if unqualified && cte_names.contains(&python_casefold(python, &relation.name)) { + continue; + } + if !extracted.relations.contains(&relation) { + extracted.relations.push(relation); + } + } + } + Ok(Some(extracted)) +} + +/// The wheel's guarded `parse`: Ok(None) is the `PolyglotError` Python catches. +fn parsed_statements( + sql: &str, + dialect: &str, +) -> Result>, RelationNamesFailure> { + let parser: Dialect = Dialect::get_by_name(dialect).ok_or_else(|| { + RelationNamesFailure::UnknownDialect(format!("Unknown dialect: {dialect}")) + })?; + let guard: ComplexityGuardOptions = + serde_json::from_value(json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH})) + .map_err(|error| RelationNamesFailure::Internal(error.to_string()))?; + let options: ParseOptions = ParseOptions { + complexity_guard: Some(guard), + }; + match parser.parse_with_options(sql, &options) { + Ok(statements) => Ok(Some(statements)), + Err(_polyglot_error) => Ok(None), + } +} + +/// Python's `_walk`: every object, popping the last pending value first. +fn walked(tree: &Value) -> Vec<&Map> { + let mut nodes: Vec<&Map> = Vec::new(); + let mut pending: Vec<&Value> = vec![tree]; + while let Some(current) = pending.pop() { + match current { + Value::Object(map) => { + nodes.push(map); + pending.extend(map.values()); + } + Value::Array(items) => pending.extend(items.iter()), + _ => {} + } + } + nodes +} + +/// Python's `_identifier`: the `name` string of an object. +fn identifier(value: Option<&Value>) -> Option<&str> { + value?.as_object()?.get(NAME_KEY)?.as_str() +} + +/// Python's `_table_names` for one node. +fn table_name(node: &Map) -> Option { + if !TABLE_KEYS.iter().all(|key| node.contains_key(*key)) { + return None; + } + let name: &str = identifier(node.get(NAME_KEY))?; + let schema: Option<&str> = identifier(node.get(SCHEMA_KEY)); + let database: Option<&str> = identifier(node.get(CATALOG_KEY)); + if schema.is_none() && database.is_none() && name.contains(QUALIFIER_SEPARATOR) { + let parts: Vec<&str> = name.split(QUALIFIER_SEPARATOR).collect(); + return Some(RelationName { + name: parts[parts.len() - 1].to_owned(), + schema: Some(parts[parts.len() - 2].to_owned()), + database: parts + .len() + .checked_sub(3) + .map(|index| parts[index].to_owned()), + }); + } + Some(RelationName { + name: name.to_owned(), + schema: schema.map(str::to_owned), + database: database.map(str::to_owned), + }) +} + +/// Python's `_cte_names` for one node, casefolded. +fn cte_names(python: PythonText, node: &Map) -> Vec { + let Some(Value::Array(ctes)) = node.get(CTE_LIST_KEY) else { + return Vec::new(); + }; + ctes.iter() + .filter_map(|cte| identifier(cte.as_object()?.get(CTE_ALIAS_KEY))) + .map(|alias| python_casefold(python, alias)) + .collect() +} + +/// Python's `_temporary_table_names` for one node, casefolded. +fn temporary_name(python: PythonText, node: &Map) -> Option { + let create: &Map = node.get(CREATE_TABLE_KEY)?.as_object()?; + if create.get(TEMPORARY_KEY) != Some(&Value::Bool(true)) { + return None; + } + let table: &Map = create.get(NAME_KEY)?.as_object()?; + identifier(table.get(NAME_KEY)).map(|name| python_casefold(python, name)) +} diff --git a/crates/sqlbuild-analysis/src/relation_names/constants.rs b/crates/sqlbuild-analysis/src/relation_names/constants.rs new file mode 100644 index 0000000000..f8cab78a94 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/constants.rs @@ -0,0 +1,15 @@ +//! Payload keys and parse limits Python's relation-name walk reads. + +/// Keys every table payload carries; Python's `_TABLE_KEYS`. +pub(crate) const TABLE_KEYS: [&str; 5] = ["name", "schema", "catalog", "alias", "hints"]; +pub(crate) const NAME_KEY: &str = "name"; +pub(crate) const SCHEMA_KEY: &str = "schema"; +pub(crate) const CATALOG_KEY: &str = "catalog"; +pub(crate) const CTE_LIST_KEY: &str = "ctes"; +pub(crate) const CTE_ALIAS_KEY: &str = "alias"; +pub(crate) const CREATE_TABLE_KEY: &str = "create_table"; +pub(crate) const TEMPORARY_KEY: &str = "temporary"; +pub(crate) const QUALIFIER_SEPARATOR: char = '.'; +pub(crate) const GENERIC_DIALECT: &str = "generic"; +/// The guarded proxy's `maxFunctionCallDepth` for the wheel's `parse`. +pub(crate) const MAX_FUNCTION_CALL_DEPTH: usize = 512; diff --git a/crates/sqlbuild-analysis/src/relation_names/errors.rs b/crates/sqlbuild-analysis/src/relation_names/errors.rs new file mode 100644 index 0000000000..223fa4c588 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/errors.rs @@ -0,0 +1,8 @@ +//! Why relation-name extraction fails. + +/// The wheel's `ValueError` for an unknown dialect, or an internal native failure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RelationNamesFailure { + UnknownDialect(String), + Internal(String), +} diff --git a/crates/sqlbuild-analysis/src/relation_names/main/extract_relation_names.rs b/crates/sqlbuild-analysis/src/relation_names/main/extract_relation_names.rs new file mode 100644 index 0000000000..54c5de8840 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/main/extract_relation_names.rs @@ -0,0 +1,13 @@ +//! Python's `extract_relation_names_impl`. + +use crate::relation_names::_helpers::walk::extracted_relations; +use crate::relation_names::errors::RelationNamesFailure; +use crate::relation_names::models::ExtractedRelations; + +/// Relations `sql` names and temporary tables it creates; None where the parse fails. +pub fn extract_relation_names( + sql: &str, + dialect: Option<&str>, +) -> Result, RelationNamesFailure> { + extracted_relations(sql, dialect) +} diff --git a/crates/sqlbuild-analysis/src/relation_names/main/mod.rs b/crates/sqlbuild-analysis/src/relation_names/main/mod.rs new file mode 100644 index 0000000000..13e389d148 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/main/mod.rs @@ -0,0 +1 @@ +pub mod extract_relation_names; diff --git a/crates/sqlbuild-analysis/src/relation_names/mod.rs b/crates/sqlbuild-analysis/src/relation_names/mod.rs new file mode 100644 index 0000000000..42fadd7136 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/mod.rs @@ -0,0 +1,7 @@ +//! Python's hard-coded relation-name extraction over the parsed statements' dict walk. + +pub(crate) mod _helpers; +mod constants; +pub mod errors; +pub mod main; +pub mod models; diff --git a/crates/sqlbuild-analysis/src/relation_names/models.rs b/crates/sqlbuild-analysis/src/relation_names/models.rs new file mode 100644 index 0000000000..e778893775 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_names/models.rs @@ -0,0 +1,16 @@ +//! Relations named in SQL, as Python's `RelationName` holds them. + +/// One relation a statement names, with the qualifiers it spells. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RelationName { + pub name: String, + pub schema: Option, + pub database: Option, +} + +/// Relations in first-seen order and the casefolded temporary tables the SQL creates. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ExtractedRelations { + pub relations: Vec, + pub temporary: Vec, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs index 762d17f3ef..ab9f02c8f4 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs @@ -4,4 +4,5 @@ pub(crate) mod normalization; pub(crate) mod normalization_results; mod positions; pub(crate) mod queries; +pub(crate) mod relation_names; pub(crate) mod sql_tests; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs index c824541f2f..da1fd8f727 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs @@ -71,6 +71,7 @@ fn analyze_project_queries_compact_json(py: Python<'_>, request_json: &str) -> P } pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + crate::bindings::_helpers::analysis::relation_names::register(module)?; module.add_function(wrap_pyfunction!(query_fingerprint, module)?)?; module.add_function(wrap_pyfunction!(schema_validation_json, module)?)?; module.add_function(wrap_pyfunction!(schema_validations_json, module)?)?; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/relation_names.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/relation_names.rs new file mode 100644 index 0000000000..8e33cf14cf --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/relation_names.rs @@ -0,0 +1,55 @@ +//! Relations named in SQL and temporary tables it creates, for hard-coded name checks. + +use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::{PyErr, pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::relation_names::errors::RelationNamesFailure; +use sqlbuild_analysis::relation_names::main::extract_relation_names::extract_relation_names; +use sqlbuild_analysis::relation_names::models::{ExtractedRelations, RelationName}; +use sqlbuild_core::panics::main::native_failure::native_failure; + +use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::types::CompilerDetach; + +const RELATION_NAMES_CONTEXT: &str = "native relation name extraction"; + +/// `(name, schema, database)`. +type RelationRow = (String, Option, Option); + +/// Python's `extract_relation_names_impl`: `(relations, temporary names)` or None if unparseable. +#[pyfunction] +#[pyo3(signature = (sql, dialect))] +fn extract_sql_relation_names( + py: Python<'_>, + sql: &str, + dialect: Option<&str>, +) -> PyResult, Vec)>> { + let extracted = py + .compiler_detach(|| Ok(extract_relation_names(sql, dialect))) + .map_err(compiler_error)? + .map_err(relation_names_error)?; + Ok(extracted.map( + |ExtractedRelations { + relations, + temporary, + }| { (relations.into_iter().map(relation_row).collect(), temporary) }, + )) +} + +fn relation_row(relation: RelationName) -> RelationRow { + (relation.name, relation.schema, relation.database) +} + +/// An unknown dialect as the wheel's `ValueError`; anything else as an internal failure. +fn relation_names_error(failure: RelationNamesFailure) -> PyErr { + match failure { + RelationNamesFailure::UnknownDialect(message) => compiler_error(message), + RelationNamesFailure::Internal(reason) => { + compiler_error(native_failure(RELATION_NAMES_CONTEXT, &reason)) + } + } +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(extract_sql_relation_names, module)?)?; + Ok(()) +} diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index e529f56d29..c72d5fcb75 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -332,6 +332,9 @@ def scan_macro_call_sites( list[tuple[int, int, int, str, list[str] | None, bool]], tuple[int | None, str | None, str] | None, ]: ... +def extract_sql_relation_names( + sql: str, dialect: str | None +) -> tuple[list[tuple[str, str | None, str | None]], list[str]] | None: ... def substitute_placeholder_defaults(sql: str, placeholders: list[tuple[str, str]], /) -> str: ... def fixture_projection_facts( sql: str, dialect: str | None, / diff --git a/src/sqlbuild/compiler/references/_helpers/relation_names.py b/src/sqlbuild/compiler/references/_helpers/relation_names.py index ca9f188d6f..8c8fb9eaf8 100644 --- a/src/sqlbuild/compiler/references/_helpers/relation_names.py +++ b/src/sqlbuild/compiler/references/_helpers/relation_names.py @@ -2,20 +2,8 @@ from __future__ import annotations -from collections.abc import Iterator -from typing import Any, cast - +import sqlbuild._native as _native from sqlbuild.compiler.references.models import ProjectRelation, ProjectRelationIndex, RelationName -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql - -_GENERIC_DIALECT: str = "generic" -_TABLE_KEYS: frozenset[str] = frozenset({"name", "schema", "catalog", "alias", "hints"}) -_CTE_LIST_KEY: str = "ctes" -_CTE_ALIAS_KEY: str = "alias" -_CREATE_TABLE_KEY: str = "create_table" -_TEMPORARY_KEY: str = "temporary" -_QUALIFIER_SEPARATOR: str = "." -_SCHEMA_QUALIFIED_PART_COUNT: int = 2 def extract_relation_names_impl( @@ -23,23 +11,19 @@ def extract_relation_names_impl( ) -> tuple[tuple[RelationName, ...], frozenset[str]] | None: """Return relations named in ``sql`` and temporary tables it creates, or None if unparseable.""" - polyglot: Any = import_polyglot_sql() - try: - statements: list[Any] = polyglot.parse(sql, dialect=dialect or _GENERIC_DIALECT) - except polyglot.PolyglotError: + extracted: tuple[list[tuple[str, str | None, str | None]], list[str]] | None = ( + _native.extract_sql_relation_names(sql, dialect) + ) + if extracted is None: return None - names: dict[RelationName, None] = {} - temporary: set[str] = set() - for statement in statements: - tree: object = statement.to_dict() - cte_names: frozenset[str] = frozenset(_cte_names(tree)) - temporary.update(_temporary_table_names(tree)) - for relation in _table_names(tree): - if relation.schema is None and relation.database is None: - if relation.name.casefold() in cte_names: - continue - names[relation] = None - return tuple(names), frozenset(temporary) + relations, temporary = extracted + return ( + tuple( + RelationName(name=name, schema=schema, database=database) + for name, schema, database in relations + ), + frozenset(temporary), + ) def match_project_relation_impl( @@ -67,72 +51,3 @@ def match_project_relation_impl( def _parts_compatible(*, left: str | None, right: str | None) -> bool: return left is None or right is None or left.casefold() == right.casefold() - - -def _walk(node: object) -> Iterator[dict[str, object]]: - pending: list[object] = [node] - while pending: - current: object = pending.pop() - if isinstance(current, dict): - mapping: dict[str, object] = cast(dict[str, object], current) - yield mapping - pending.extend(mapping.values()) - elif isinstance(current, list | tuple): - pending.extend(current) - - -def _identifier(value: object) -> str | None: - if isinstance(value, dict): - name: object = cast(dict[str, object], value).get("name") - return name if isinstance(name, str) else None - return None - - -def _table_names(tree: object) -> Iterator[RelationName]: - for node in _walk(tree): - if not _TABLE_KEYS.issubset(node.keys()): - continue - name: str | None = _identifier(node.get("name")) - if name is None: - continue - schema: str | None = _identifier(node.get("schema")) - database: str | None = _identifier(node.get("catalog")) - if schema is None and database is None and _QUALIFIER_SEPARATOR in name: - parts: list[str] = name.split(_QUALIFIER_SEPARATOR) - name = parts[-1] - schema = parts[-2] - database = parts[-3] if len(parts) > _SCHEMA_QUALIFIED_PART_COUNT else None - yield RelationName(name=name, schema=schema, database=database) - - -def _cte_names(tree: object) -> Iterator[str]: - for node in _walk(tree): - ctes: object = node.get(_CTE_LIST_KEY) - if not isinstance(ctes, list): - continue - for cte in ctes: - alias: str | None = ( - _identifier(cast(dict[str, object], cte).get(_CTE_ALIAS_KEY)) - if isinstance(cte, dict) - else None - ) - if alias is not None: - yield alias.casefold() - - -def _temporary_table_names(tree: object) -> Iterator[str]: - for node in _walk(tree): - create: object = node.get(_CREATE_TABLE_KEY) - if not isinstance(create, dict): - continue - create_table: dict[str, object] = cast(dict[str, object], create) - if create_table.get(_TEMPORARY_KEY) is not True: - continue - table: object = create_table.get("name") - name: str | None = ( - _identifier(cast(dict[str, object], table).get("name")) - if isinstance(table, dict) - else None - ) - if name is not None: - yield name.casefold() diff --git a/tests/goldens/python_oracle/relation_names_hard_coded_relation_edges.json b/tests/goldens/python_oracle/relation_names_hard_coded_relation_edges.json new file mode 100644 index 0000000000..697a5f89ba --- /dev/null +++ b/tests/goldens/python_oracle/relation_names_hard_coded_relation_edges.json @@ -0,0 +1,90 @@ +[ +[ +"relation_names", +"97441cac529281e0cc24ca91" +], +[ +"relation_names", +"6f6f17d74a2d182dcca715ec" +], +[ +"relation_names", +"97441cac529281e0cc24ca91" +], +[ +"relation_names", +"acde9a115a0ac703bd3bd3df" +], +[ +"relation_names", +"694deb3886fa7a4e11705220" +], +[ +"relation_names", +"fd0c7b14bc139764127543a4" +], +[ +"relation_names", +"a180f2e81ae38757d5ea840f" +], +[ +"relation_names", +"f39469d6dfe3c4cfac5789b7" +], +[ +"relation_names", +"e3d90a5ec29167469993355c" +], +[ +"relation_names", +"74234e98afe7498fb5daf1f3" +], +[ +"relation_names", +"9806faa1ab31111cd5e23f82" +], +[ +"relation_names", +"dbcb2fcbc05164b64760b437" +], +[ +"relation_names", +"5d90e126c6a171a0fbfe84a1" +], +[ +"relation_names", +"055cec2fa05c4ab0814c218f" +], +[ +"relation_names", +"8c3a356da4dd363b47295607" +], +[ +"relation_names", +"250261fc57ae6a030cc265bb" +], +[ +"relation_names", +"ef3355ab56c0f05febbf677c" +], +[ +"relation_names", +"9806faa1ab31111cd5e23f82" +], +[ +"relation_names", +"bf001523fcaedb3d64a3d9a3" +], +[ +"relation_names", +"c272291db0662682b6651d41" +], +[ +"relation_names", +"9806faa1ab31111cd5e23f82" +], +[ +"relation_names", +"9bc7c81e3b3ef877d35f3b22" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/sql_analysis/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_analysis/_test_types.py index 5af0584a8f..53de15e7e9 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_analysis/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_analysis/_test_types.py @@ -13,3 +13,12 @@ class SiteRecorderTestCase: record_dir: str expected_rows: tuple[tuple[str, str, int], ...] expected_entry_point: str + + +@dataclass(frozen=True) +class RelationNamesTestCase: + """SQL texts whose hard-coded relation names Python's walk recorded in a golden.""" + + description: str + queries: tuple[tuple[str, str | None], ...] + expected_differences: list[tuple[int, str, object, object]] diff --git a/tests/integration/src/sqlbuild/compiler/sql_analysis/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_analysis/helpers.py index 6f91e00bbe..5aee5106b2 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_analysis/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_analysis/helpers.py @@ -4,8 +4,12 @@ import json from pathlib import Path +from typing import cast +from sqlbuild.compiler.references.main.extract_relation_names import extract_relation_names +from sqlbuild.compiler.references.models import RelationName from sqlbuild.compiler.sql_analysis.constants import POLYGLOT_SITE_RECORD_PREFIX +from tests.integration.src.sqlbuild.compiler.compile.helpers import ExpansionFailureCapture def recorded_rows(root: Path) -> tuple[tuple[str, str, int], ...]: @@ -16,3 +20,37 @@ def recorded_rows(root: Path) -> tuple[tuple[str, str, int], ...]: for site, api, count in json.loads(path.read_text(encoding="utf-8"))["calls"]: rows.append((str(site), str(api), int(count))) return tuple(rows) + + +def relation_names_outcome(sql: str, dialect: str | None) -> object: + """The extracted `(relations, sorted temporary names)`, None if unparseable, or the error.""" + + capture: ExpansionFailureCapture = ExpansionFailureCapture() + extracted: list[tuple[tuple[RelationName, ...], frozenset[str]] | None] = [None] + with capture: + extracted[0] = extract_relation_names(sql=sql, dialect=dialect) + return { + True: lambda: _relation_view(extracted[0]), + False: lambda: (type(capture.failure).__name__, str(capture.failure)), + }[capture.failure is None]() + + +def _relation_view( + extracted: tuple[tuple[RelationName, ...], frozenset[str]] | None, +) -> object: + return { + True: lambda: None, + False: lambda: _present_relation_view( + cast(tuple[tuple[RelationName, ...], frozenset[str]], extracted) + ), + }[extracted is None]() + + +def _present_relation_view( + extracted: tuple[tuple[RelationName, ...], frozenset[str]], +) -> tuple[tuple[tuple[str, str | None, str | None], ...], tuple[str, ...]]: + relations, temporary = extracted + return ( + tuple((relation.name, relation.schema, relation.database) for relation in relations), + tuple(sorted(temporary)), + ) diff --git a/tests/integration/src/sqlbuild/compiler/sql_analysis/test_native_relation_names.py b/tests/integration/src/sqlbuild/compiler/sql_analysis/test_native_relation_names.py new file mode 100644 index 0000000000..d863a5f9c2 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/sql_analysis/test_native_relation_names.py @@ -0,0 +1,87 @@ +"""Hard-coded relation names come from the native parse and match Python's dict walk.""" + +from __future__ import annotations + +import pytest + +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) +from tests.integration.src.sqlbuild.compiler.sql_analysis._test_types import ( + RelationNamesTestCase, +) +from tests.integration.src.sqlbuild.compiler.sql_analysis.helpers import relation_names_outcome + + +@pytest.mark.parametrize( + "test_case", + [ + RelationNamesTestCase( + description="hard coded relation edges", + queries=( + ("SELECT * FROM orders", None), + ( + "SELECT * FROM analytics.orders o JOIN db.sales.customers c ON o.id = c.id", + "duckdb", + ), + ( + "WITH Recent AS (SELECT * FROM orders) SELECT * FROM recent JOIN RECENT r2 ON 1=1", + "duckdb", + ), + ("WITH recent AS (SELECT 1) SELECT * FROM s.recent", "duckdb"), + ( + "CREATE TEMPORARY TABLE Staging AS SELECT * FROM raw.events; INSERT INTO staging SELECT * FROM raw.events", + "duckdb", + ), + ("CREATE TEMP TABLE t1 (id INT); SELECT * FROM T1", "postgres"), + ("SELECT * FROM `proj.dataset.table` JOIN `a.b` USING (id)", "bigquery"), + ('SELECT * FROM "Weird.Name"', "duckdb"), + ("SELECT * FROM a.b.c.d", "duckdb"), + ("SELECT (", "duckdb"), + ("", "duckdb"), + ("SELECT 1; SELECT * FROM x; SELECT * FROM x", ""), + ("UPDATE inventory SET qty = 0 WHERE id IN (SELECT id FROM products)", "snowflake"), + ( + "DELETE FROM sales.orders USING customers WHERE orders.cid = customers.id", + "postgres", + ), + ( + "MERGE INTO tgt USING src ON tgt.id = src.id WHEN MATCHED THEN UPDATE SET v = src.v", + "snowflake", + ), + ("SELECT * FROM [dbo].[Orders]", "sqlserver"), + ("SELECT * FROM Größe, ÉTÉ", "duckdb"), + ("WITH ÉTÉ AS (SELECT 1) SELECT * FROM été", "duckdb"), + ("CREATE TABLE perm AS SELECT 1", "duckdb"), + ("SELECT * FROM orders", "motherduck"), + ("SELECT * FROM read_csv('x.csv'), generate_series(1, 3)", "duckdb"), + ( + "SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id) UNION SELECT * FROM t3", + "databricks", + ), + ), + expected_differences=[], + ), + ], + ids=lambda case: case.description, +) +def test_given_python_node_sql_when_extracting_relation_names_then_matches_python_golden( + test_case: RelationNamesTestCase, +) -> None: + outcomes: list[object] = [ + relation_names_outcome(sql, dialect) for sql, dialect in test_case.queries + ] + expected: list[GoldenEntry] = read_golden(golden_name("relation_names", test_case.description)) + + assert ( + golden_differences(expected, [golden_entry("relation_names", item) for item in outcomes]) + == test_case.expected_differences + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/sql_analysis/test_polyglot_site_recorder.py b/tests/integration/src/sqlbuild/compiler/sql_analysis/test_polyglot_site_recorder.py index de009bcc7f..8f926f2bea 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_analysis/test_polyglot_site_recorder.py +++ b/tests/integration/src/sqlbuild/compiler/sql_analysis/test_polyglot_site_recorder.py @@ -14,15 +14,14 @@ from tests.integration.src.sqlbuild.compiler.sql_analysis.helpers import recorded_rows _PROCESS: str = ( - "from sqlbuild.compiler.references.main.extract_relation_names import " - "extract_relation_names\n" + "from sqlbuild.rule_engine.classes.sql_document import SqlDocument\n" "from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql\n" "for _ in range(2):\n" - " assert extract_relation_names(sql='SELECT 1 FROM main.orders', dialect='duckdb')\n" + " assert SqlDocument(source='SELECT 1 FROM main.orders', dialect='duckdb').polyglot_ast()\n" "module = import_polyglot_sql()\n" "print(type(module.parse_one).__name__, isinstance(module.PolyglotError, type))\n" ) -_SITE: str = "compiler/references/_helpers/relation_names.py:extract_relation_names_impl" +_SITE: str = "rule_engine/classes/sql_document.py:SqlDocument.polyglot_ast" @pytest.mark.parametrize( @@ -31,7 +30,7 @@ SiteRecorderTestCase( description="recording", record_dir="records", - expected_rows=((_SITE, "parse", 2),), + expected_rows=((_SITE, "parse_one", 2),), expected_entry_point="_RecordedEntryPoint", ), SiteRecorderTestCase( From f0bb556c9db884ca74630ae81a040f73ce05940b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:34:16 +0100 Subject: [PATCH 29/36] feat(native): read SQL-test helper CTE reads from the native parse The helper CTE graph's parsed reads (unqualified tables naming a helper key, minus CTEs a body defines at any depth) come from the native parse and Python's dict walk port instead of the wheel. A golden from Python's _parsed_reads covers qualification, shadowing, quoting, non-ASCII keys, set operations, subqueries and parse failures. --- .../analysis_session/_helpers/dict_walk.rs | 66 +++++++++++++++++-- .../assembly/analysis_session/constants.rs | 7 ++ .../src/assembly/analysis_session/main/mod.rs | 1 + .../main/sql_test_helper_reads.rs | 8 +++ .../bindings/_helpers/analysis/sql_tests.rs | 23 ++++++- src/sqlbuild/_native.pyi | 1 + .../compile/_helpers/sql_tests/helper_ctes.py | 44 +------------ .../helper_reads_helper_cte_read_edges.json | 58 ++++++++++++++++ .../compiler/sql_test_glue/_test_types.py | 10 +++ .../compiler/sql_test_glue/helpers.py | 7 ++ .../sql_test_glue/test_native_helper_reads.py | 60 +++++++++++++++++ 11 files changed, 238 insertions(+), 47 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/sql_test_helper_reads.rs create mode 100644 tests/goldens/python_oracle/helper_reads_helper_cte_read_edges.json create mode 100644 tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_helper_reads.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs index b0c8573ed6..51afc0030c 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs @@ -7,15 +7,17 @@ use serde_json::{Map, Value, json}; use sqlbuild_core::text::main::active_python_text::active_python_text; use sqlbuild_core::text::main::python_casefold::python_casefold; use sqlbuild_core::text::main::python_upper::python_upper; +use sqlbuild_core::text::models::PythonText; use crate::assembly::analysis_session::models::ProjectionFacts; use crate::assembly::analysis_session::constants::{ - ALIAS_AST_KIND, ANNOTATED_AST_KIND, ARRAY_FIXTURE_TYPE, ARRAY_PAYLOAD_KIND, COLUMN_AST_KIND, - DEPENDENCY_FUNCTION_NAMES, FUNCTION_AST_KIND, FUNCTION_FIXTURE_TYPES, FUNCTION_PAYLOAD_KIND, - LITERAL_FIXTURE_TYPES, LITERAL_PAYLOAD_KIND, MAX_FUNCTION_CALL_DEPTH, NULL_PAYLOAD_KIND, - RENDERED_TYPE_NAMES, SELECT_PAYLOAD_KEY, STAR_AST_KIND, STRUCTURED_PAYLOAD_KINDS, - TABLE_AST_KIND, VARIANT_FIXTURE_TYPE, WILDCARD, + ALIAS_AST_KIND, ANNOTATED_AST_KIND, ARRAY_FIXTURE_TYPE, ARRAY_PAYLOAD_KIND, CATALOG_TABLE_ARG, + COLUMN_AST_KIND, CTE_ALIAS_PAYLOAD_KEY, CTE_LIST_PAYLOAD_KEY, DEPENDENCY_FUNCTION_NAMES, + FUNCTION_AST_KIND, FUNCTION_FIXTURE_TYPES, FUNCTION_PAYLOAD_KIND, HELPER_READS_DIALECT, + LITERAL_FIXTURE_TYPES, LITERAL_PAYLOAD_KIND, MAX_FUNCTION_CALL_DEPTH, NAME_PAYLOAD_KEY, + NULL_PAYLOAD_KIND, RENDERED_TYPE_NAMES, SCHEMA_TABLE_ARG, SELECT_PAYLOAD_KEY, STAR_AST_KIND, + STRUCTURED_PAYLOAD_KINDS, TABLE_AST_KIND, VARIANT_FIXTURE_TYPE, WILDCARD, }; /// Python's `bool(value)`. @@ -545,3 +547,57 @@ fn unique(names: Vec) -> Vec { } kept } + +/// Python's `_parsed_reads`: helper keys `sql` reads, first first; None where `parse_one` raises. +pub(crate) fn helper_parsed_reads( + sql: &str, + keys: &[String], +) -> Result>, String> { + let python: PythonText = active_python_text(); + let folded: String = python_casefold(python, sql); + if !keys.iter().any(|key| folded.contains(key.as_str())) { + return Ok(Some(Vec::new())); + } + let Some(parsed) = parse_one(sql, HELPER_READS_DIALECT)? else { + return Ok(None); + }; + let nested: Vec = defined_cte_keys(python, &to_dict(&parsed)?); + let mut reads: Vec = Vec::new(); + for table in find_all(&parsed, TABLE_AST_KIND) { + if arg(Some(table), SCHEMA_TABLE_ARG)?.is_some() + || arg(Some(table), CATALOG_TABLE_ARG)?.is_some() + { + continue; + } + let key: String = python_casefold(python, name(Some(table))); + if keys.contains(&key) && !nested.contains(&key) && !reads.contains(&key) { + reads.push(key); + } + } + Ok(Some(reads)) +} + +/// Python's `_defined_cte_keys`: every CTE alias at any depth, casefolded. +fn defined_cte_keys(python: PythonText, tree: &Value) -> Vec { + let mut keys: Vec = Vec::new(); + let mut pending: Vec<&Value> = vec![tree]; + while let Some(value) = pending.pop() { + match value { + Value::Array(items) => pending.extend(items.iter()), + Value::Object(map) => { + if let Some(Value::Array(ctes)) = map.get(CTE_LIST_PAYLOAD_KEY) { + keys.extend(ctes.iter().filter_map(|cte| { + let alias: &Value = cte.as_object()?.get(CTE_ALIAS_PAYLOAD_KEY)?; + Some(python_casefold( + python, + alias.as_object()?.get(NAME_PAYLOAD_KEY)?.as_str()?, + )) + })); + } + pending.extend(map.values()); + } + _ => {} + } + } + keys +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index 16817bdaa0..9417d4071e 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -227,3 +227,10 @@ pub(crate) const FUNCTION_FIXTURE_TYPES: [(&str, &str); 3] = [ ]; pub(crate) const ARRAY_FIXTURE_TYPE: &str = "ARRAY"; pub(crate) const VARIANT_FIXTURE_TYPE: &str = "VARIANT"; +/// Dialect, payload keys and table args Python's SQL-test `_parsed_reads` reads. +pub(crate) const HELPER_READS_DIALECT: &str = "generic"; +pub(crate) const CTE_LIST_PAYLOAD_KEY: &str = "ctes"; +pub(crate) const CTE_ALIAS_PAYLOAD_KEY: &str = "alias"; +pub(crate) const NAME_PAYLOAD_KEY: &str = "name"; +pub(crate) const SCHEMA_TABLE_ARG: &str = "schema"; +pub(crate) const CATALOG_TABLE_ARG: &str = "catalog"; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 97d081ef54..a2aee74abf 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -10,5 +10,6 @@ pub mod query_columns; pub mod recover_cte_facts; pub mod run_analysis_session; pub mod session_sharing; +pub mod sql_test_helper_reads; pub mod start_analysis_session; pub mod take_analysis_cache; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/sql_test_helper_reads.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/sql_test_helper_reads.rs new file mode 100644 index 0000000000..cae1fdcf3b --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/sql_test_helper_reads.rs @@ -0,0 +1,8 @@ +//! Unqualified reads of SQL-test helper CTE keys in one CTE body. + +use crate::assembly::analysis_session::_helpers::dict_walk::helper_parsed_reads; + +/// The `keys` (casefolded) `sql` reads, first read first; None where the SQL does not parse. +pub fn sql_test_helper_reads(sql: &str, keys: &[String]) -> Result>, String> { + helper_parsed_reads(sql, keys) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs index 4a35f05267..179c323596 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/sql_tests.rs @@ -4,10 +4,14 @@ use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; -use crate::bindings::_helpers::boundary::panics::value_error; +use sqlbuild_core::panics::main::native_failure::native_failure; + +use crate::bindings::_helpers::boundary::panics::{compiler_error, value_error}; use crate::bindings::_helpers::sqltext::lexical_syntax::LexicalSyntaxInput; use crate::bindings::types::CompilerDetach; +const HELPER_READS_CONTEXT: &str = "native SQL-test helper reads"; + #[pyfunction] fn render_sql_test_comparisons_json(py: Python<'_>, request_json: &str) -> PyResult { py.compiler_detach(|| { @@ -69,7 +73,24 @@ fn function_type_error( .map_err(value_error) } +/// Python's `_parsed_reads`: the helper keys a CTE body reads, or None if it does not parse. +#[pyfunction] +fn sql_test_helper_reads( + py: Python<'_>, + sql: &str, + keys: Vec, +) -> PyResult>> { + py.compiler_detach(|| { + sqlbuild_analysis::assembly::analysis_session::main::sql_test_helper_reads::sql_test_helper_reads( + sql, &keys, + ) + .map_err(|reason| native_failure(HELPER_READS_CONTEXT, &reason)) + }) + .map_err(compiler_error) +} + pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(sql_test_helper_reads, module)?)?; module.add_function(wrap_pyfunction!(function_type_error, module)?)?; module.add_function(wrap_pyfunction!(extract_sql_scenario_json, module)?)?; module.add_function(wrap_pyfunction!(render_sql_test_comparisons_json, module)?)?; diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index c72d5fcb75..f33f671ee5 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -332,6 +332,7 @@ def scan_macro_call_sites( list[tuple[int, int, int, str, list[str] | None, bool]], tuple[int | None, str | None, str] | None, ]: ... +def sql_test_helper_reads(sql: str, keys: list[str], /) -> list[str] | None: ... def extract_sql_relation_names( sql: str, dialect: str | None ) -> tuple[list[tuple[str, str | None, str | None]], list[str]] | None: ... diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py index 82822230b0..fa806f2e8b 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/helper_ctes.py @@ -4,8 +4,8 @@ import re from collections.abc import Iterable -from typing import Any +import sqlbuild._native as _native from sqlbuild.compiler.compile._helpers.diagnostics.collector import report_compile_diagnostic from sqlbuild.compiler.compile._helpers.refs.references import ( reference_call_location, @@ -34,7 +34,6 @@ ) from sqlbuild.compiler.discovery.models import DiscoveredSqlTestBlock, DiscoveredSqlTestFile from sqlbuild.compiler.references.types import SqlReferenceKind -from sqlbuild.compiler.sql_analysis.main.import_polyglot_sql import import_polyglot_sql from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax from sqlbuild.spec.contracts.models import SourceLocation @@ -332,45 +331,8 @@ def _references(*, sql: str, syntax: SqlLexicalSyntax) -> tuple[CompileSqlRefere def _parsed_reads(*, sql: str, keys: frozenset[str]) -> tuple[str, ...] | None: """Return unqualified table reads of ``keys`` not shadowed by a nested CTE, or None.""" - folded: str = sql.casefold() - if not any(key in folded for key in keys): - return () - polyglot_module: Any = import_polyglot_sql() - try: - parsed: Any = polyglot_module.parse_one(sql, dialect="generic") - except polyglot_module.PolyglotError: - return None - nested: frozenset[str] = _defined_cte_keys(parsed.to_dict()) - reads: list[str] = [] - for table in parsed.find_all("table"): - if table.arg("schema") is not None or table.arg("catalog") is not None: - continue - key: str = str(getattr(table, "name", "") or "").casefold() - if key in keys and key not in nested and key not in reads: - reads.append(key) - return tuple(reads) - - -def _defined_cte_keys(tree: Any) -> frozenset[str]: - """Return the case-folded names of every CTE a parsed query defines, at any depth.""" - - keys: set[str] = set() - pending: list[Any] = [tree] - while pending: - value: Any = pending.pop() - if isinstance(value, list): - pending.extend(value) - continue - if not isinstance(value, dict): - continue - ctes: Any = value.get("ctes") - for cte in ctes if isinstance(ctes, list) else (): - alias: Any = cte.get("alias") if isinstance(cte, dict) else None - name: Any = alias.get("name") if isinstance(alias, dict) else None - if isinstance(name, str): - keys.add(name.casefold()) - pending.extend(value.values()) - return frozenset(keys) + reads: list[str] | None = _native.sql_test_helper_reads(sql, sorted(keys)) + return None if reads is None else tuple(reads) def _token_reads(*, sql: str, keys: frozenset[str]) -> tuple[str, ...]: diff --git a/tests/goldens/python_oracle/helper_reads_helper_cte_read_edges.json b/tests/goldens/python_oracle/helper_reads_helper_cte_read_edges.json new file mode 100644 index 0000000000..0bdad0d555 --- /dev/null +++ b/tests/goldens/python_oracle/helper_reads_helper_cte_read_edges.json @@ -0,0 +1,58 @@ +[ +[ +"helper_reads", +"aa8cdea031c78169cfcfc8cb" +], +[ +"helper_reads", +"7af39b7194de7feac7d049f8" +], +[ +"helper_reads", +"6786f18f6ff42aa577c99d86" +], +[ +"helper_reads", +"77d5413fc4891929962834c2" +], +[ +"helper_reads", +"aa8cdea031c78169cfcfc8cb" +], +[ +"helper_reads", +"6786f18f6ff42aa577c99d86" +], +[ +"helper_reads", +"6786f18f6ff42aa577c99d86" +], +[ +"helper_reads", +"74234e98afe7498fb5daf1f3" +], +[ +"helper_reads", +"4e5d6ec7534ff80d3cf04e61" +], +[ +"helper_reads", +"aa8cdea031c78169cfcfc8cb" +], +[ +"helper_reads", +"ed29b0da213826917a1d4a41" +], +[ +"helper_reads", +"6786f18f6ff42aa577c99d86" +], +[ +"helper_reads", +"74234e98afe7498fb5daf1f3" +], +[ +"helper_reads", +"77d5413fc4891929962834c2" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py index 28207b6a74..f7f45a42ef 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py @@ -24,3 +24,13 @@ class EdgeSqlTestAssemblyTestCase: description: str files: dict[str, str] expected_outcome: str + + +@dataclass(frozen=True) +class HelperReadsTestCase: + """CTE bodies whose helper-key reads Python's `_parsed_reads` recorded in a golden.""" + + description: str + keys: frozenset[str] + bodies: tuple[str, ...] + expected_differences: list[tuple[int, str, object, object]] diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index 8091acb997..21f55641e9 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -526,3 +526,10 @@ def _outcome(call: Callable[[], object]) -> PlanningCallOutcome: return PlanningCallOutcome(value=call(), raised=None) except Exception as error: # noqa: BLE001 - both paths must raise the same error return PlanningCallOutcome(value=None, raised=(type(error).__name__, str(error))) + + +def helper_reads_view(sql: str, keys: frozenset[str]) -> tuple[str, ...] | None: + """The native helper reads of `sql` as Python's `_parsed_reads` returned them.""" + + reads: list[str] | None = native_module.sql_test_helper_reads(sql, sorted(keys)) + return {True: lambda: None, False: lambda: tuple(cast(list[str], reads))}[reads is None]() diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_helper_reads.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_helper_reads.py new file mode 100644 index 0000000000..7e12a8a639 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_helper_reads.py @@ -0,0 +1,60 @@ +"""SQL-test helper CTE reads come from the native parse and match Python's `_parsed_reads`.""" + +from __future__ import annotations + +import pytest + +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) +from tests.integration.src.sqlbuild.compiler.sql_test_glue._test_types import HelperReadsTestCase +from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import helper_reads_view + + +@pytest.mark.parametrize( + "test_case", + [ + HelperReadsTestCase( + description="helper cte read edges", + keys=frozenset({"base", "helper", "mock_orders", "étapé"}), + bodies=( + "SELECT * FROM base", + "SELECT * FROM BASE JOIN helper h ON 1=1 JOIN base b2 ON 1=1", + "SELECT * FROM s.base JOIN db.s.helper ON 1=1", + "WITH base AS (SELECT 1) SELECT * FROM base, helper", + "SELECT * FROM (WITH Helper AS (SELECT 1) SELECT * FROM helper) x JOIN base ON 1=1", + "SELECT 1", + "SELECT * FROM unrelated", + "SELECT * FROM base WHERE (", + "SELECT * FROM ÉTAPÉ", + 'SELECT * FROM "Base"', + "SELECT * FROM mock_orders UNION ALL SELECT * FROM helper WHERE id IN (SELECT id FROM base)", + "SELECT base FROM other", + "SELECT * FROM helper; SELECT * FROM base", + "SELECT * FROM t WHERE EXISTS (SELECT 1 FROM helper)", + ), + expected_differences=[], + ), + ], + ids=lambda case: case.description, +) +def test_given_cte_bodies_when_reading_helper_keys_then_matches_python_golden( + test_case: HelperReadsTestCase, +) -> None: + views: list[tuple[str, ...] | None] = [ + helper_reads_view(body, test_case.keys) for body in test_case.bodies + ] + expected: list[GoldenEntry] = read_golden(golden_name("helper_reads", test_case.description)) + + assert ( + golden_differences(expected, [golden_entry("helper_reads", view) for view in views]) + == test_case.expected_differences + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) From be1188abfc4283d90cfea6f4c187608b7aad3033 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:41:21 +0100 Subject: [PATCH 30/36] fix(native)!: raise native project assembly panics instead of retrying in Python A parser panic during native project assembly or the native syntax check used to be reported as a 'panic' deferral, so Python re-ran the whole assembly through the wheel, which crashes the process on the same SQL. Both now raise NativeCompilerError with their context, with no Python retry and no deferral record. --- .../src/assembly/project/constants.rs | 1 - .../main/assemble_project_resources.rs | 12 +---- .../assembly/project/main/check_sql_syntax.rs | 12 +---- .../_helpers/project_assembly/resources.rs | 25 +++++++--- .../compiler/project_assembly/_test_types.py | 10 ++++ .../test_native_project_assembly.py | 49 +++++++++++++++++++ 6 files changed, 82 insertions(+), 27 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/project/constants.rs b/crates/sqlbuild-analysis/src/assembly/project/constants.rs index e190f17f47..13ffe10457 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/constants.rs @@ -37,4 +37,3 @@ pub(crate) const NORMALIZATION_DEFERRAL: &str = "normalization"; /// Python's message when `validate` rejects SQL without naming an error. pub(crate) const INVALID_SQL: &str = "invalid SQL"; pub(crate) const UNSUPPORTED_DIALECT_DEFERRAL: &str = "unsupported_dialect"; -pub(crate) const PANIC_DEFERRAL: &str = "panic"; diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs index e873ff0692..87bac39655 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs @@ -2,26 +2,18 @@ use std::cell::RefCell; -use sqlbuild_core::constants::PANIC_MESSAGE; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::project::_helpers::deps::{audit_deps, reference_deps}; use crate::assembly::project::_helpers::syntax::syntax_valid; use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; use crate::assembly::project::_helpers::templates::TemplateInputs; -use crate::assembly::project::constants::PANIC_DEFERRAL; use crate::assembly::project::models::{ModelFacts, ProjectRequest, ProjectResources}; use crate::assembly::project::types::Fact; -/// Each resource's dependencies and namespace, or the kind of work Python must do instead. +/// Each resource's dependencies and namespace; Err is a deferral kind or a native failure. pub fn assemble_project_resources(request: &ProjectRequest) -> Result { - catch_compiler_panic(|| resources(request)).map_err(|kind| { - if kind == PANIC_MESSAGE { - PANIC_DEFERRAL.to_owned() - } else { - kind - } - }) + catch_compiler_panic(|| resources(request)) } fn resources(request: &ProjectRequest) -> Fact { diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs index a2604be080..bda9d3e5ff 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs @@ -1,21 +1,13 @@ //! Whether Python's syntax validation accepts every SQL string, stopping at the first it rejects. -use sqlbuild_core::constants::PANIC_MESSAGE; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::project::_helpers::syntax::syntax_valid; -use crate::assembly::project::constants::PANIC_DEFERRAL; use crate::assembly::project::models::SyntaxCheck; -/// True when every check parses, false at the first Python rejects, Err to let Python decide. +/// Whether every check parses; Err is a deferral kind or a native failure. pub fn check_sql_syntax(dialect: &str, checks: &[SyntaxCheck]) -> Result { - catch_compiler_panic(|| all_valid(dialect, checks)).map_err(|kind| { - if kind == PANIC_MESSAGE { - PANIC_DEFERRAL.to_owned() - } else { - kind - } - }) + catch_compiler_panic(|| all_valid(dialect, checks)) } fn all_valid(dialect: &str, checks: &[SyntaxCheck]) -> Result { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index 92572c287d..94f026666a 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -16,11 +16,16 @@ use sqlbuild_analysis::assembly::project::models::{ TargetNamespace, Variable, }; use sqlbuild_analysis::assembly::project::types::ObjectKey; +use sqlbuild_core::panics::main::is_compiler_panic::is_compiler_panic; +use sqlbuild_core::panics::main::native_failure::native_failure; use sqlbuild_model_config::templates::models::Scalar; use crate::bindings::_helpers::boundary::panics::compiler_error; use crate::bindings::types::CompilerDetach; +const PROJECT_ASSEMBLY_CONTEXT: &str = "native project assembly"; +const SYNTAX_CHECK_CONTEXT: &str = "native SQL syntax check"; + const TEXT_VARIABLE: &str = "text"; const BOOLEAN_VARIABLE: &str = "bool"; const NONE_VARIABLE: &str = "none"; @@ -93,11 +98,15 @@ type ResourcesRow = ( fn assemble_project_resource_facts( py: Python<'_>, request: RequestRow, -) -> (Option, Option) { +) -> PyResult<(Option, Option)> { let request: ProjectRequest = project_request(request); match py.compiler_detach(|| assemble_project_resources(&request)) { - Ok(resources) => (Some(resources_row(resources)), None), - Err(reason) => (None, Some(reason)), + Ok(resources) => Ok((Some(resources_row(resources)), None)), + Err(reason) if is_compiler_panic(&reason) => Err(compiler_error(native_failure( + PROJECT_ASSEMBLY_CONTEXT, + &reason, + ))), + Err(reason) => Ok((None, Some(reason))), } } @@ -138,15 +147,19 @@ fn sql_syntax_error( fn check_native_sql_syntax( py: Python<'_>, request: (String, Vec<(String, Pairs)>), -) -> (Option, Option) { +) -> PyResult<(Option, Option)> { let (dialect, rows) = request; let checks: Vec = rows .into_iter() .map(|(sql, placeholders)| SyntaxCheck { sql, placeholders }) .collect(); match py.compiler_detach(|| check_sql_syntax(&dialect, &checks)) { - Ok(valid) => (Some(valid), None), - Err(reason) => (None, Some(reason)), + Ok(valid) => Ok((Some(valid), None)), + Err(reason) if is_compiler_panic(&reason) => Err(compiler_error(native_failure( + SYNTAX_CHECK_CONTEXT, + &reason, + ))), + Err(reason) => Ok((None, Some(reason))), } } diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py index 1c63ba08d6..96437c806e 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py @@ -66,3 +66,13 @@ class GraphFactsTestCase: expected_model_tags: tuple[list[str], ...] expected_seeds: list[tuple[str, list[str]]] expected_function_kinds: tuple[str, ...] + + +@dataclass(frozen=True) +class NativePanicTestCase: + """A project whose native assembly panics in the parser.""" + + description: str + files: dict[str, str] + dialect: str + expected_message: str diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index ffd4e5a78f..185b175337 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -10,8 +10,12 @@ import pytest +import sqlbuild._native as native_module +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.compiler.compile._helpers.assembly.project import assemble_compiled_project from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import CompiledProject, CompileProjectInputs +from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.compiler.project_assembly.types import ProjectRequestRow from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR @@ -21,6 +25,7 @@ DeferredAssemblyTestCase, GeneratedAssemblyParityTestCase, GraphFactsTestCase, + NativePanicTestCase, OptOutTestCase, WindowsEnvironmentTestCase, ) @@ -397,3 +402,47 @@ def test_given_project_when_assembling_natively_then_request_carries_graph_facts ], ) assert [kind for kind, _, _, _ in request[8]] == list(test_case.expected_function_kinds) + + +@pytest.mark.parametrize( + "test_case", + [ + NativePanicTestCase( + description="a T-SQL hook the parser panics on", + files={ + "sqlbuild_project.toml": _PROJECT_TOML, + "models/orders.sql": ( + 'MODEL (\n description "Orders",\n' + ' pre_hooks [inline_sql("SELECT IF(region > 1, re")],\n);\n\nSELECT 1 AS n\n' + ), + }, + dialect="tsql", + expected_message=( + "NativeCompilerError: native SQL compilation panicked (native project assembly)" + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_native_assembly_panics_when_assembling_then_raises_without_python_retry( + test_case: NativePanicTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE.value) + inputs: CompileProjectInputs = project_inputs( + project_dir=tmp_path / "project", files=test_case.files + ) + calls: Counter[str] = python_resource_calls(monkeypatch=monkeypatch) + + with pytest.raises(native_module.NativeCompilerError) as raised: + _ = assemble_compiled_project( + inputs=inputs, + inference_profile=ExpressionInferenceProfile(sql_analysis_dialect=test_case.dialect), + ) + + assert (str(raised.value), calls, assembly_deferrals(record_dir)) == ( + test_case.expected_message, + Counter(), + Counter(), + ) From 7417e47982a668a1f3cfa8466fb633736b779d94 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:48:39 +0100 Subject: [PATCH 31/36] feat(native): compose a model's final columns natively in Python's order ModelColumnsInput::model_columns applies the session result, the parse fallback for a failed analysis, the session or standalone pivot proof and type-recovery poisoning in _assemble_model's and _recovered_model's order, so the owner store can finalize typed model columns without Python. SessionModelAnalysis now reports whether the analysis succeeded. --- .../src/assembly/analysis_session/models.rs | 72 +++++++- .../src/assembly/analysis_session/tests.rs | 2 + .../analysis_session/tests/helpers.rs | 45 ++++- .../tests/test_model_columns.rs | 162 ++++++++++++++++++ .../analysis_session/tests/test_types.rs | 18 ++ 5 files changed, 292 insertions(+), 7 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_model_columns.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index ab8170f337..bd0a141b75 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -104,6 +104,8 @@ pub struct FinishedSession { /// The session's typed result for one compiled model, borrowed from the finished session. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SessionModelAnalysis<'a> { + /// Whether the session's analysis of the model succeeded; false also for unanalysed names. + pub succeeded: bool, /// A proven pivot's fixed columns, else a successful analysis's; None where later decided. pub columns: Option<&'a [ColumnFact]>, /// The session's dynamic pivot proof, for a model declaring dynamic families. @@ -118,11 +120,10 @@ impl FinishedSession { let proven: Option<&[ColumnFact]> = dynamic_proof .filter(|proof| proof.output_proven) .map(|proof| proof.fixed_columns.as_slice()); - let analysed: Option<&[ColumnFact]> = self - .models - .get(name) - .and_then(|facts| facts.columns.as_deref()); + let facts: Option<&SessionModelFacts> = self.models.get(name); + let analysed: Option<&[ColumnFact]> = facts.and_then(|facts| facts.columns.as_deref()); SessionModelAnalysis { + succeeded: facts.is_some(), columns: proven.or(analysed), dynamic_proof, } @@ -400,3 +401,66 @@ pub struct ProjectionFacts { pub null_literal_names: Vec, pub quoted_names: Vec, } + +/// One model's analysis results, which `model_columns` applies in `_assemble_model`'s order. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ModelColumnsInput<'a> { + /// The model's analysis is enabled (`sql_analysis_enabled` for this model). + pub analysis_enabled: bool, + /// The finished session's result for the model. + pub session: SessionModelAnalysis<'a>, + /// The `Parse` mode `query_columns` result, required when enabled and the session failed. + pub fallback: Option<&'a QueryColumns>, + /// A standalone pivot proof, for a model with families the session did not analyse. + pub standalone_proof: Option<&'a ContractProof>, + /// Output columns type recovery poisoned for this model (`TypeRecoveryPlan::poisoned`). + pub poisoned: &'a [String], +} + +/// A model's final output columns, as `CompiledModel` carries them after type recovery. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ModelColumns { + /// `inferred_columns`; a poisoned column has no type. + pub columns: Option>, + /// A proven pivot forces `fast_lineage_has_star`. + pub proven_star: bool, + /// `unchecked_output_columns`: every poisoned name, in plan order. + pub unchecked: Vec, +} + +impl ModelColumnsInput<'_> { + /// `_assemble_model`'s columns, then `_recovered_model`'s; Err where the fallback is missing. + pub fn model_columns(&self) -> Result { + let analysed: Option> = if !self.analysis_enabled { + None + } else if self.session.succeeded { + self.session.columns.map(<[ColumnFact]>::to_vec) + } else { + self.fallback + .ok_or("a failed session analysis without its parse fallback")? + .columns + .clone() + }; + let proven: Option<&ContractProof> = self + .session + .dynamic_proof + .or(self.standalone_proof) + .filter(|proof| proof.output_proven); + let columns: Option> = proven + .map(|proof| proof.fixed_columns.clone()) + .or(analysed) + .map(|mut columns| { + for column in &mut columns { + if self.poisoned.contains(&column.name) { + column.data_type = None; + } + } + columns + }); + Ok(ModelColumns { + columns, + proven_star: proven.is_some(), + unchecked: self.poisoned.to_vec(), + }) + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests.rs index a3cfec2ac5..f9737f30b6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests.rs @@ -8,5 +8,7 @@ mod expression_shapes; mod helpers; #[path = "tests/test_mappings.rs"] mod mappings; +#[path = "tests/test_model_columns.rs"] +mod model_columns; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 9e5b1a0127..16b5f60f76 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -17,11 +17,13 @@ use crate::assembly::analysis_session::main::start_analysis_session::start_analy use crate::assembly::analysis_session::main::take_analysis_cache::take_analysis_cache; use crate::assembly::analysis_session::models::{ AnalysisCacheStats, AnalysisSession, ColumnFact, ContractProof, DynamicFamily, FinishedSession, - LineageRow, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, - PivotOutcome, PivotTables, SessionModelAnalysis, SessionModelFacts, SessionOutcome, + LineageRow, ModelColumns, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, + PivotModel, PivotOutcome, PivotTables, SessionModelAnalysis, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, }; -use crate::assembly::analysis_session::tests::test_types::{CachedRun, ModelSpec, RecoveredFacts}; +use crate::assembly::analysis_session::tests::test_types::{ + CachedRun, ColumnView, ModelSpec, RecoveredFacts, +}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::{CatalogInput, ProjectCatalog}; @@ -615,3 +617,40 @@ pub(crate) fn current_model_key(session: &AnalysisSession, name: &str) -> Conten pub(crate) fn lines(expected: &[&str]) -> Vec { expected.iter().map(|line| (*line).to_owned()).collect() } + +/// `(name, type)` pairs as typed columns of unknown nullability. +pub(crate) fn fact_columns(pairs: &[(&str, &str)]) -> Vec { + pairs + .iter() + .map(|(name, data_type)| ColumnFact { + name: (*name).to_owned(), + data_type: Some((*data_type).to_owned()), + nullability: "unknown".to_owned(), + }) + .collect() +} + +/// A pivot proof with these fixed columns. +pub(crate) fn contract_proof((output_proven, fixed): (bool, &[(&str, &str)])) -> ContractProof { + ContractProof { + output_proven, + fixed_columns: fact_columns(fixed), + families: Vec::new(), + input_relations: Vec::new(), + failure_reason: None, + bare_dynamic_pivot: false, + } +} + +/// Final columns as `(name, type)` pairs and the forced star, for comparison with expectations. +pub(crate) fn column_view(finished: &ModelColumns) -> ColumnView<'_> { + ( + finished.columns.as_ref().map(|columns| { + columns + .iter() + .map(|column| (column.name.as_str(), column.data_type.as_deref())) + .collect() + }), + finished.proven_star, + ) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_model_columns.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_model_columns.rs new file mode 100644 index 0000000000..1f8f081497 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_model_columns.rs @@ -0,0 +1,162 @@ +use crate::assembly::analysis_session::models::{ + ColumnFact, ContractProof, ModelColumns, ModelColumnsInput, QueryColumns, SessionModelAnalysis, +}; +use crate::assembly::analysis_session::tests::helpers::{ + column_view, contract_proof, fact_columns, +}; +use crate::assembly::analysis_session::tests::test_types::ModelColumnsTestCase; + +const ORDERS: &[(&str, &str)] = &[("id", "INTEGER"), ("amount", "DECIMAL")]; +const PARSED: &[(&str, &str)] = &[("id", "BIGINT")]; +const PIVOT: &[(&str, &str)] = &[("region", "VARCHAR"), ("north", "DECIMAL")]; + +#[test] +fn given_model_results_when_finalizing_columns_then_applies_python_order() { + let test_cases = [ + ModelColumnsTestCase { + description: "analysis disabled keeps no columns but still reports poisoned names", + analysis_enabled: false, + succeeded: false, + session_columns: None, + session_proof: None, + fallback: None, + standalone_proof: None, + poisoned: &["id"], + expected_columns: Ok((None, false)), + }, + ModelColumnsTestCase { + description: "a successful session keeps its columns and type recovery unsets poisoned types", + analysis_enabled: true, + succeeded: true, + session_columns: Some(ORDERS), + session_proof: None, + fallback: None, + standalone_proof: None, + poisoned: &["amount"], + expected_columns: Ok((Some(vec![("id", Some("INTEGER")), ("amount", None)]), false)), + }, + ModelColumnsTestCase { + description: "a failed session takes the parse fallback's columns", + analysis_enabled: true, + succeeded: false, + session_columns: None, + session_proof: None, + fallback: Some(Some(PARSED)), + standalone_proof: None, + poisoned: &[], + expected_columns: Ok((Some(vec![("id", Some("BIGINT"))]), false)), + }, + ModelColumnsTestCase { + description: "a failed session whose parse fallback infers nothing has no columns", + analysis_enabled: true, + succeeded: false, + session_columns: None, + session_proof: None, + fallback: Some(None), + standalone_proof: None, + poisoned: &[], + expected_columns: Ok((None, false)), + }, + ModelColumnsTestCase { + description: "a failed session without its parse fallback is an internal failure", + analysis_enabled: true, + succeeded: false, + session_columns: None, + session_proof: None, + fallback: None, + standalone_proof: None, + poisoned: &[], + expected_columns: Err("a failed session analysis without its parse fallback"), + }, + ModelColumnsTestCase { + description: "a proven session pivot replaces the parse fallback and forces a star", + analysis_enabled: true, + succeeded: false, + session_columns: None, + session_proof: Some((true, PIVOT)), + fallback: Some(Some(PARSED)), + standalone_proof: None, + poisoned: &[], + expected_columns: Ok(( + Some(vec![ + ("region", Some("VARCHAR")), + ("north", Some("DECIMAL")), + ]), + true, + )), + }, + ModelColumnsTestCase { + description: "an unproven session pivot keeps the analysed columns", + analysis_enabled: true, + succeeded: true, + session_columns: Some(ORDERS), + session_proof: Some((false, PIVOT)), + fallback: None, + standalone_proof: Some((true, PIVOT)), + poisoned: &[], + expected_columns: Ok(( + Some(vec![("id", Some("INTEGER")), ("amount", Some("DECIMAL"))]), + false, + )), + }, + ModelColumnsTestCase { + description: "a proven standalone pivot of an unanalysed model sets columns, then poisoning", + analysis_enabled: false, + succeeded: false, + session_columns: None, + session_proof: None, + fallback: None, + standalone_proof: Some((true, PIVOT)), + poisoned: &["north"], + expected_columns: Ok(( + Some(vec![("region", Some("VARCHAR")), ("north", None)]), + true, + )), + }, + ]; + for test_case in test_cases { + let session_columns: Option> = test_case.session_columns.map(fact_columns); + let session_proof: Option = test_case.session_proof.map(contract_proof); + let fallback: Option = test_case.fallback.map(|parsed| QueryColumns { + succeeded: parsed.is_some(), + columns: parsed.map(fact_columns), + has_star: false, + }); + let standalone_proof: Option = + test_case.standalone_proof.map(contract_proof); + let poisoned: Vec = test_case + .poisoned + .iter() + .map(|name| (*name).to_owned()) + .collect(); + let input = ModelColumnsInput { + analysis_enabled: test_case.analysis_enabled, + session: SessionModelAnalysis { + succeeded: test_case.succeeded, + columns: session_proof + .as_ref() + .filter(|proof| proof.output_proven) + .map(|proof| proof.fixed_columns.as_slice()) + .or(session_columns.as_deref()), + dynamic_proof: session_proof.as_ref(), + }, + fallback: fallback.as_ref(), + standalone_proof: standalone_proof.as_ref(), + poisoned: &poisoned, + }; + + let finished: Result = input.model_columns(); + + assert_eq!( + finished.as_ref().map(column_view).map_err(String::as_str), + test_case.expected_columns, + "{}", + test_case.description + ); + assert!( + finished.map_or(true, |finished| finished.unchecked == poisoned), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs index 8eeca03aa4..cf81b59c4f 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs @@ -152,3 +152,21 @@ pub(crate) struct KeptAnalysisTestCase { /// `name type nullability` of each model's published columns: the pivot, then a plain one. pub(crate) expected_columns: [&'static [&'static str]; 2], } + +/// Final `(name, type)` columns and the forced star. +pub(crate) type ColumnView<'a> = (Option)>>, bool); + +/// The expected column view, or the internal failure. +pub(crate) type ExpectedModelColumns = Result, &'static str>; + +pub(crate) struct ModelColumnsTestCase { + pub(crate) description: &'static str, + pub(crate) analysis_enabled: bool, + pub(crate) succeeded: bool, + pub(crate) session_columns: Option<&'static [(&'static str, &'static str)]>, + pub(crate) session_proof: Option<(bool, &'static [(&'static str, &'static str)])>, + pub(crate) fallback: Option>, + pub(crate) standalone_proof: Option<(bool, &'static [(&'static str, &'static str)])>, + pub(crate) poisoned: &'static [&'static str], + pub(crate) expected_columns: ExpectedModelColumns, +} From 87e83e97f8283cadbda061e664cd8350941278e8 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:39:48 +0100 Subject: [PATCH 32/36] feat(native): raise Python's project assembly errors natively without deferring Native project assembly now reports the first source, seed or audit Python's assembly raises at, with Python's exact CompileInputError/ValueError messages, and renders variables only Python renders from their precomputed str() and render_project_var_text outcomes. Python's target, dependency and fallback helpers are deleted; the goldens record the pre-port Python outcomes. --- .../src/assembly/project/_helpers/deps.rs | 14 +- .../src/assembly/project/_helpers/mod.rs | 1 + .../assembly/project/_helpers/resources.rs | 99 +++++ .../src/assembly/project/_helpers/syntax.rs | 15 +- .../src/assembly/project/_helpers/targets.rs | 81 +++- .../assembly/project/_helpers/templates.rs | 125 ++++-- .../src/assembly/project/constants.rs | 20 +- .../src/assembly/project/errors.rs | 17 + .../main/assemble_project_resources.rs | 65 +-- .../assembly/project/main/check_sql_syntax.rs | 13 +- .../src/assembly/project/mod.rs | 1 + .../src/assembly/project/models.rs | 41 +- .../src/assembly/project/tests/helpers.rs | 18 +- .../project/tests/test_project_assembly.rs | 19 +- .../src/assembly/project/tests/test_types.rs | 4 +- .../src/assembly/project/types.rs | 6 +- crates/sqlbuild-core/src/text/main/mod.rs | 1 + .../src/text/main/python_str_repr.rs | 33 ++ .../_helpers/project_assembly/resources.rs | 182 +++++--- .../src/scope_index/_helpers/identities.rs | 33 +- .../src/scope_index/main/scope_lookup.rs | 4 +- .../scope_index/tests/test_scope_lookup.rs | 2 +- .../native_fallbacks.toml | 42 -- src/sqlbuild/_native.pyi | 25 +- .../compile/_helpers/assembly/project.py | 181 ++------ .../compile/_helpers/assembly/targets.py | 184 -------- .../compile/_helpers/deps/dependencies.py | 133 ------ .../diagnostics/sql_analysis_opt_outs.py | 26 +- .../_helpers/native_stages/assembly.py | 14 +- src/sqlbuild/compiler/frontier/types.py | 1 - .../project_assembly/_helpers/deferrals.py | 24 - .../project_assembly/_helpers/request.py | 58 ++- .../project_assembly/_helpers/resources.py | 54 ++- .../compiler/project_assembly/constants.py | 15 +- .../_assemble_native_project_resources.py | 23 +- .../main/_check_native_sql_syntax.py | 14 +- .../project_assembly/main/_native_resource.py | 16 + .../compiler/project_assembly/models.py | 18 +- .../compiler/project_assembly/types.py | 4 +- .../scripts/compiler_differential/helpers.py | 6 - .../test_native_fallback_gate.py | 12 - ...assembly_error_and_python_value_edges.json | 122 +++++ ...s_and_targets_hooks_audits_a_function.json | 34 ++ .../compiler/project_assembly/_test_types.py | 15 +- .../compiler/project_assembly/helpers.py | 420 +++++++++++++++--- .../test_native_project_assembly.py | 242 ++++------ .../compiler/compile/_helpers/_test_types.py | 24 - .../compiler/compile/_helpers/test_deps.py | 152 ------- .../frontier/test_native_fallback_recorder.py | 10 +- .../compiler/planner/_helpers/helpers.py | 32 +- 50 files changed, 1373 insertions(+), 1322 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/project/_helpers/resources.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/project/errors.rs create mode 100644 crates/sqlbuild-core/src/text/main/python_str_repr.rs delete mode 100644 src/sqlbuild/compiler/compile/_helpers/deps/dependencies.py delete mode 100644 src/sqlbuild/compiler/project_assembly/_helpers/deferrals.py create mode 100644 src/sqlbuild/compiler/project_assembly/main/_native_resource.py create mode 100644 tests/goldens/python_oracle/project_assembly_error_and_python_value_edges.json create mode 100644 tests/goldens/python_oracle/project_assembly_managed_sources_templated_seeds_and_targets_hooks_audits_a_function.json delete mode 100644 tests/unit/src/sqlbuild/compiler/compile/_helpers/test_deps.py diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/deps.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/deps.rs index da8005a6de..de5150607b 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/deps.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/deps.rs @@ -2,9 +2,12 @@ use std::collections::HashSet; +use sqlbuild_core::text::main::python_str_repr::python_str_repr; + use crate::assembly::project::constants::{ - ATTACHED_AUDIT_KINDS, AUDIT_TARGET_DEFERRAL, DBT_REF_KIND, KIND_RESOURCES, MODEL_RESOURCE, + ATTACHED_AUDIT_KINDS, AUDIT_TARGET_KIND_ENUM, DBT_REF_KIND, KIND_RESOURCES, MODEL_RESOURCE, }; +use crate::assembly::project::errors::AssemblyError; use crate::assembly::project::models::{AuditFacts, Reference}; use crate::assembly::project::types::Fact; use crate::assembly::project::types::ObjectKey; @@ -19,7 +22,14 @@ pub(crate) fn audit_deps(audit: &AuditFacts) -> Fact> { let mut keys: Vec = reference_deps(&audit.references); if let Some((kind, name)) = &audit.attached { if !ATTACHED_AUDIT_KINDS.contains(&kind.as_str()) { - return Err(AUDIT_TARGET_DEFERRAL.to_owned()); + return Err(match python_str_repr(kind) { + Some(repr) => { + AssemblyError::Value(format!("{repr} is not a valid {AUDIT_TARGET_KIND_ENUM}")) + } + None => AssemblyError::Internal(format!( + "a non-ASCII attached audit target kind is not a valid {AUDIT_TARGET_KIND_ENUM}" + )), + }); } keys.push((kind.clone(), name.clone())); } diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/mod.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/mod.rs index 000b88358d..b16c7ba17f 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod deps; +pub(crate) mod resources; pub(crate) mod syntax; pub(crate) mod targets; pub(crate) mod templates; diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/resources.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/resources.rs new file mode 100644 index 0000000000..2cf2a12302 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/resources.rs @@ -0,0 +1,99 @@ +//! Python's resource assembly loop over one project's sources, seeds and audits. + +use std::cell::RefCell; + +use crate::assembly::project::_helpers::deps::{audit_deps, reference_deps}; +use crate::assembly::project::_helpers::syntax::syntax_valid; +use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; +use crate::assembly::project::_helpers::templates::TemplateInputs; +use crate::assembly::project::models::{ + ModelFacts, ProjectRequest, ProjectResources, ResourceFailure, ResourceSlot, +}; + +/// Each resource's facts in Python's assembly order (sources, seeds, audits) up to its failure. +pub(crate) fn project_resources(request: &ProjectRequest) -> ProjectResources { + let target = request.target.as_ref(); + let inputs = TemplateInputs { + variables: &request.variables, + environment: &request.environment, + reads: RefCell::new(Vec::new()), + }; + let mut resources = ProjectResources { + model_syntax_valid: request + .models + .iter() + .map(|model| valid_syntax(model, &request.dialect)) + .collect(), + model_deps: request + .models + .iter() + .map(|model| reference_deps(&model.references)) + .collect(), + function_deps: request + .functions + .iter() + .map(|function| reference_deps(&function.references)) + .collect(), + ..ProjectResources::default() + }; + for (index, source) in request.sources.iter().enumerate() { + match managed_source(source, target, &inputs) { + Ok(namespace) => resources.sources.push(namespace), + Err(error) => { + let failure = ResourceFailure { + slot: ResourceSlot::Source, + index, + error, + }; + return failed(resources, &inputs, failure); + } + } + } + for (index, seed) in request.seeds.iter().enumerate() { + match seed_target(seed, &request.defaults, target, &inputs) { + Ok(namespace) => resources.seeds.push(namespace), + Err(error) => { + let failure = ResourceFailure { + slot: ResourceSlot::Seed, + index, + error, + }; + return failed(resources, &inputs, failure); + } + } + } + for (index, audit) in request.audits.iter().enumerate() { + match audit_deps(audit) { + Ok(keys) => resources.audit_deps.push(keys), + Err(error) => { + let failure = ResourceFailure { + slot: ResourceSlot::Audit, + index, + error, + }; + return failed(resources, &inputs, failure); + } + } + } + resources.reads = inputs.reads.into_inner(); + resources +} + +/// The resources assembled so far, the reads made and the resource Python raises at. +fn failed( + mut resources: ProjectResources, + inputs: &TemplateInputs<'_>, + failure: ResourceFailure, +) -> ProjectResources { + resources.reads = inputs.reads.borrow().clone(); + resources.failure = Some(failure); + resources +} + +/// Whether Python's model and hook syntax validation passes; Python reports every failure. +fn valid_syntax(model: &ModelFacts, dialect: &str) -> bool { + model + .syntax_checks + .iter() + .all(|check| syntax_valid(check, dialect) == Ok(true)) +} diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs index ad475e58be..b9706b03d1 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/syntax.rs @@ -7,24 +7,17 @@ use polyglot_sql::{ validate_with_dialect, }; -use crate::assembly::project::constants::{ - INVALID_SQL, MAX_FUNCTION_CALL_DEPTH, NORMALIZATION_DEFERRAL, UNSUPPORTED_DIALECT_DEFERRAL, -}; +use crate::assembly::project::constants::{INVALID_SQL, MAX_FUNCTION_CALL_DEPTH}; use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure, SyntaxMode}; -use crate::assembly::project::types::Fact; use crate::lineage::main::parser_dialect::parser_dialect; use crate::semantic_validation::main::normalize::normalize_analysis_sql; use crate::semantic_validation::models::NormalizationInput; -/// Whether Polyglot's `validate` accepts the cleaned SQL; Python raises where it does not. -pub(crate) fn syntax_valid(check: &SyntaxCheck, dialect: &str) -> Fact { - match syntax_error(check, dialect, SyntaxMode::Validate) { - Ok(error) => Ok(error.is_none()), - Err(SyntaxFailure::Normalization(_)) => Err(NORMALIZATION_DEFERRAL.to_owned()), - Err(SyntaxFailure::UnknownDialect(_)) => Err(UNSUPPORTED_DIALECT_DEFERRAL.to_owned()), - } +/// Whether Polyglot's `validate` accepts the cleaned SQL; Err is the `ValueError` Python raises. +pub(crate) fn syntax_valid(check: &SyntaxCheck, dialect: &str) -> Result { + syntax_error(check, dialect, SyntaxMode::Validate).map(|error| error.is_none()) } /// Python's syntax error message for one SQL string, None where Polyglot accepts it. diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/targets.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/targets.rs index 5a8cdeeaca..ff59b71794 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/targets.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/targets.rs @@ -1,11 +1,16 @@ //! Python's managed source and seed targets (`assembly/project.py`, `assembly/targets.py`). use crate::assembly::project::_helpers::templates::{Context, TemplateInputs, expanded_text}; +use crate::assembly::project::constants::{ + DATABASE_DIMENSION, DEFAULT_SEED_DATABASE_LABEL, DEFAULT_SEED_SCHEMA_LABEL, + MANAGED_SOURCE_TARGET_LABEL, SCHEMA_DIMENSION, TARGET_DATABASE_LABEL, TARGET_SCHEMA_LABEL, +}; use crate::assembly::project::constants::{ DESTINATION_DATABASE_CONTEXT, DESTINATION_QUALIFIED_CONTEXT, DESTINATION_SCHEMA_CONTEXT, DESTINATION_TABLE_CONTEXT, MODEL_ALIAS_CONTEXT, MODEL_DATABASE_CONTEXT, MODEL_NAME_CONTEXT, - MODEL_SCHEMA_CONTEXT, PRESERVE_TARGET_VALUE, PRESERVED_NAMESPACE_DEFERRAL, + MODEL_SCHEMA_CONTEXT, PRESERVE_TARGET_VALUE, }; +use crate::assembly::project::errors::AssemblyError; use crate::assembly::project::models::{ Namespace, SeedDefaults, SeedFacts, SourceFacts, TargetNamespace, }; @@ -31,6 +36,7 @@ pub(crate) fn managed_source( loader_schema: target.loader_schema.clone(), }; preserved_namespace( + &format!("Managed source '{}'", source.name), Some(&loader_target), source.database.as_ref(), source.schema.as_ref(), @@ -59,25 +65,44 @@ pub(crate) fn seed_target( .as_ref() .or(defaults.database.as_ref()), inputs, + DEFAULT_SEED_DATABASE_LABEL, )?; let mut logical_schema: Option = environment_text( defaults.seed_schema.as_ref().or(defaults.schema.as_ref()), inputs, + DEFAULT_SEED_SCHEMA_LABEL, )?; if let Some(raw) = &seed.database { let context = seed_context(&seed.name, logical_database.clone(), logical_schema.clone()); - logical_database = seed_text(raw, inputs, &context)?; + let label: String = format!("seed '{}' database", seed.name); + logical_database = seed_text(raw, inputs, &context, &label)?; } if let Some(raw) = &seed.schema { let context = seed_context(&seed.name, logical_database.clone(), logical_schema.clone()); - logical_schema = seed_text(raw, inputs, &context)?; + let label: String = format!("seed '{}' schema", seed.name); + logical_schema = seed_text(raw, inputs, &context, &label)?; } - preserved_namespace(target, logical_database.as_ref(), logical_schema.as_ref())?; + preserved_namespace( + &format!("Seed '{}'", seed.name), + target, + logical_database.as_ref(), + logical_schema.as_ref(), + )?; let (database, schema) = match target { None => (logical_database.clone(), logical_schema.clone()), Some(target) => ( - target_override(target.database.as_ref(), logical_database.clone(), inputs)?, - target_override(target.schema.as_ref(), logical_schema.clone(), inputs)?, + target_override( + target.database.as_ref(), + logical_database.clone(), + inputs, + TARGET_DATABASE_LABEL, + )?, + target_override( + target.schema.as_ref(), + logical_schema.clone(), + inputs, + TARGET_SCHEMA_LABEL, + )?, ), }; Ok(Namespace { @@ -88,8 +113,9 @@ pub(crate) fn seed_target( }) } -/// `validate_preserved_logical_namespace`; Python raises where Err. +/// `validate_preserved_logical_namespace` for the resource Python names `resource_label`. fn preserved_namespace( + resource_label: &str, target: Option<&TargetNamespace>, logical_database: Option<&String>, logical_schema: Option<&String>, @@ -99,27 +125,46 @@ fn preserved_namespace( }; let preserved = |value: Option<&String>| value.is_some_and(|value| value == PRESERVE_TARGET_VALUE); - if (preserved(target.database.as_ref()) && logical_database.is_none()) - || (preserved(target.schema.as_ref()) && logical_schema.is_none()) - { - return Err(PRESERVED_NAMESPACE_DEFERRAL.to_owned()); + let mut missing: Vec<&str> = Vec::new(); + if preserved(target.database.as_ref()) && logical_database.is_none() { + missing.push(DATABASE_DIMENSION); + } + if preserved(target.schema.as_ref()) && logical_schema.is_none() { + missing.push(SCHEMA_DIMENSION); + } + if missing.is_empty() { + return Ok(()); } - Ok(()) + let dimensions: String = missing.join(" and "); + Err(AssemblyError::CompileInput { + message: format!( + "{resource_label} has no logical {dimensions}, but the selected target sets \ + {dimensions} to '{PRESERVE_TARGET_VALUE}'" + ), + help: Some(format!( + "Set {dimensions} on the resource or its defaults, or configure a literal target \ + {dimensions}." + )), + }) } /// `_expand_target_value`. fn optional_text(value: Option<&str>, inputs: &TemplateInputs<'_>) -> Fact> { value - .map(|value| expanded_text(value, inputs, None)) + .map(|value| expanded_text(value, inputs, None, MANAGED_SOURCE_TARGET_LABEL)) .transpose() } /// `_expand_seed_default_value` and `_expand_seed_environment_value`. -fn environment_text(raw: Option<&String>, inputs: &TemplateInputs<'_>) -> Fact> { +fn environment_text( + raw: Option<&String>, + inputs: &TemplateInputs<'_>, + label: &str, +) -> Fact> { match raw { None => Ok(None), Some(raw) if raw == PRESERVE_TARGET_VALUE => Ok(None), - Some(raw) => expanded_text(raw, inputs, None).map(Some), + Some(raw) => expanded_text(raw, inputs, None, label).map(Some), } } @@ -128,11 +173,12 @@ fn seed_text( raw: &str, inputs: &TemplateInputs<'_>, context: &Context<'_>, + label: &str, ) -> Fact> { if raw == PRESERVE_TARGET_VALUE { return Ok(None); } - expanded_text(raw, inputs, Some(context)).map(Some) + expanded_text(raw, inputs, Some(context), label).map(Some) } /// `_apply_seed_target_overrides` for one dimension. @@ -140,9 +186,10 @@ fn target_override( raw: Option<&String>, logical: Option, inputs: &TemplateInputs<'_>, + label: &str, ) -> Fact> { match raw { - Some(raw) if raw != PRESERVE_TARGET_VALUE => environment_text(Some(raw), inputs), + Some(raw) if raw != PRESERVE_TARGET_VALUE => environment_text(Some(raw), inputs, label), _ => Ok(logical), } } diff --git a/crates/sqlbuild-analysis/src/assembly/project/_helpers/templates.rs b/crates/sqlbuild-analysis/src/assembly/project/_helpers/templates.rs index f97b281c3a..2356aea56e 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/_helpers/templates.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/_helpers/templates.rs @@ -1,18 +1,21 @@ -//! Python's `str(expand_template_data(...))` over scalar variables, the environment and context. +//! Python's `str(expand_template_data(...))` over project variables, the environment and context. -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; +use sqlbuild_model_config::templates::errors::TemplateError; use sqlbuild_model_config::templates::main::expand_template_string::expand_template_string; +use sqlbuild_model_config::templates::main::template_error_message::template_error_message; use sqlbuild_model_config::templates::models::{ ContextValue, Scalar, StringExpansion, TemplateFailure, TemplateOptions, }; use sqlbuild_model_config::templates::types::TemplateHost; use crate::assembly::project::constants::{ - PROJECT_VAR_FALSE, PROJECT_VAR_TRUE, PYTHON_FALSE, PYTHON_NONE, PYTHON_TRUE, TEMPLATE_DEFERRAL, - TEMPLATE_LABEL, TEMPLATE_OPEN_TOKEN, + PROJECT_VAR_FALSE, PROJECT_VAR_TRUE, PYTHON_FALSE, PYTHON_NONE, PYTHON_TRUE, + TEMPLATE_OPEN_TOKEN, UNLISTED_ENVIRONMENT, }; -use crate::assembly::project::models::{InputRead, Variable}; +use crate::assembly::project::errors::AssemblyError; +use crate::assembly::project::models::{InputRead, OpaqueRender, OpaqueVariable, Variable}; use crate::assembly::project::types::Fact; /// Context values by name; None is a known key whose value Python holds as None. @@ -26,26 +29,42 @@ pub(crate) struct TemplateInputs<'a> { pub(crate) reads: RefCell>, } -/// One expansion's inputs and context values. -struct ScalarHost<'a> { +/// One evaluated value: a scalar, or a variable value only Python renders. +#[derive(Clone)] +enum TargetValue { + Scalar(Scalar), + Opaque(OpaqueVariable), +} + +/// One expansion's inputs, context values and the label its errors name. +struct TargetHost<'a> { inputs: &'a TemplateInputs<'a>, context: &'a Context<'a>, + label: &'a str, + /// Why the host stopped evaluation: the exception Python raises, or an internal failure. + stopped: Cell>, } -impl TemplateHost for ScalarHost<'_> { - type Value = Scalar; +impl TargetHost<'_> { + fn stop(&self, error: AssemblyError) -> TemplateFailure { + self.stopped.set(Some(error)); + TemplateFailure::Host + } +} + +impl TemplateHost for TargetHost<'_> { + type Value = TargetValue; fn variable(&self, name: &str) -> Result, TemplateFailure> { - match self + Ok(self .inputs .variables .iter() .find(|(variable, _)| variable == name) - { - None => Ok(None), - Some((_, Variable::Scalar(value))) => Ok(Some(value.clone())), - Some((_, Variable::Unsupported)) => Err(TemplateFailure::Host), - } + .map(|(_, value)| match value { + Variable::Scalar(value) => TargetValue::Scalar(value.clone()), + Variable::Opaque(value) => TargetValue::Opaque(value.clone()), + })) } fn environment(&self, name: &str) -> Result, TemplateFailure> { @@ -59,8 +78,12 @@ impl TemplateHost for ScalarHost<'_> { .iter() .find(|(variable, _)| variable == name) { - None => Err(TemplateFailure::Host), - Some((_, value)) => Ok(value.clone().map(Scalar::Text)), + None => Err(self.stop(AssemblyError::Internal(format!( + "{UNLISTED_ENVIRONMENT} '{name}'" + )))), + Some((_, value)) => Ok(value + .clone() + .map(|text| TargetValue::Scalar(Scalar::Text(text)))), } } @@ -72,45 +95,58 @@ impl TemplateHost for ScalarHost<'_> { Ok(match self.context.iter().find(|(key, _)| *key == name) { None => ContextValue::Unknown, Some((_, None)) => ContextValue::Unavailable, - Some((_, Some(value))) => ContextValue::Value(Scalar::Text(value.clone())), + Some((_, Some(value))) => { + ContextValue::Value(TargetValue::Scalar(Scalar::Text(value.clone()))) + } }) } fn text(&self, text: &str) -> Result { - Ok(Scalar::Text(text.to_owned())) + Ok(TargetValue::Scalar(Scalar::Text(text.to_owned()))) } fn boolean(&self, flag: bool) -> Result { - Ok(Scalar::Bool(flag)) + Ok(TargetValue::Scalar(Scalar::Bool(flag))) } fn null(&self) -> Self::Value { - Scalar::Null + TargetValue::Scalar(Scalar::Null) } fn scalar(&self, value: &Self::Value) -> Result { - Ok(value.clone()) - } - - fn render(&self, value: &Self::Value, _label: &str) -> Result { Ok(match value { - Scalar::Null => String::new(), - Scalar::Bool(true) => PROJECT_VAR_TRUE.to_owned(), - Scalar::Bool(false) => PROJECT_VAR_FALSE.to_owned(), - Scalar::Text(text) => text.clone(), + TargetValue::Scalar(value) => value.clone(), + TargetValue::Opaque(value) => Scalar::Text(value.text.clone()), }) } + fn render(&self, value: &Self::Value, label: &str) -> Result { + match value { + TargetValue::Scalar(Scalar::Null) => Ok(String::new()), + TargetValue::Scalar(Scalar::Bool(true)) => Ok(PROJECT_VAR_TRUE.to_owned()), + TargetValue::Scalar(Scalar::Bool(false)) => Ok(PROJECT_VAR_FALSE.to_owned()), + TargetValue::Scalar(Scalar::Text(text)) => Ok(text.clone()), + TargetValue::Opaque(value) => match &value.render { + OpaqueRender::Text(text) => Ok(text.clone()), + OpaqueRender::Rejected(rest) => Err(TemplateFailure::Invalid( + TemplateError::Message(format!("{label}{rest}")), + )), + OpaqueRender::Raises(index) => Err(self.stop(AssemblyError::Raise(*index))), + }, + } + } + fn label(&self) -> &str { - TEMPLATE_LABEL + self.label } } -/// `str(expand_template_data(value=text, ...))`; context is allowed only where Some. +/// `str(expand_template_data(value=text, context_label=label, ...))`; context only where Some. pub(crate) fn expanded_text( text: &str, inputs: &TemplateInputs<'_>, context: Option<&Context<'_>>, + label: &str, ) -> Fact { if !text.contains(TEMPLATE_OPEN_TOKEN) { return Ok(text.to_owned()); @@ -120,24 +156,35 @@ pub(crate) fn expanded_text( preserve_context_tokens: false, preserve_unknown_context: false, }; - let host = ScalarHost { + let host = TargetHost { inputs, context: context.unwrap_or_default(), + label, + stopped: Cell::new(None), }; match expand_template_string(&host, text, options) { Ok(StringExpansion::Unchanged) => Ok(text.to_owned()), Ok(StringExpansion::Value(value)) => Ok(python_str(value)), Ok(StringExpansion::Text(expanded)) => Ok(expanded), - Err(_) => Err(TEMPLATE_DEFERRAL.to_owned()), + Err(TemplateFailure::Missing(error) | TemplateFailure::Invalid(error)) => { + Err(AssemblyError::CompileInput { + message: template_error_message(&error, label), + help: None, + }) + } + Err(TemplateFailure::Host) => Err(host.stopped.take().unwrap_or_else(|| { + AssemblyError::Internal("template expansion stopped without an error".to_owned()) + })), } } -/// Python's `str()` of a scalar. -fn python_str(value: Scalar) -> String { +/// Python's `str()` of a value. +fn python_str(value: TargetValue) -> String { match value { - Scalar::Null => PYTHON_NONE.to_owned(), - Scalar::Bool(true) => PYTHON_TRUE.to_owned(), - Scalar::Bool(false) => PYTHON_FALSE.to_owned(), - Scalar::Text(text) => text, + TargetValue::Scalar(Scalar::Null) => PYTHON_NONE.to_owned(), + TargetValue::Scalar(Scalar::Bool(true)) => PYTHON_TRUE.to_owned(), + TargetValue::Scalar(Scalar::Bool(false)) => PYTHON_FALSE.to_owned(), + TargetValue::Scalar(Scalar::Text(text)) => text, + TargetValue::Opaque(value) => value.text, } } diff --git a/crates/sqlbuild-analysis/src/assembly/project/constants.rs b/crates/sqlbuild-analysis/src/assembly/project/constants.rs index 13ffe10457..0eedde351b 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/constants.rs @@ -25,15 +25,21 @@ pub(crate) const DESTINATION_DATABASE_CONTEXT: &str = "destination.database"; pub(crate) const DESTINATION_SCHEMA_CONTEXT: &str = "destination.schema"; pub(crate) const DESTINATION_TABLE_CONTEXT: &str = "destination.table"; pub(crate) const DESTINATION_QUALIFIED_CONTEXT: &str = "destination.qualified"; -pub(crate) const AUDIT_TARGET_DEFERRAL: &str = "audit_target_kind"; -pub(crate) const TEMPLATE_DEFERRAL: &str = "target_template"; /// `render_project_var_text` of booleans inside a larger template string. pub(crate) const PROJECT_VAR_TRUE: &str = "true"; pub(crate) const PROJECT_VAR_FALSE: &str = "false"; -/// The label template errors would name; assembly defers every template error. -pub(crate) const TEMPLATE_LABEL: &str = "project assembly template"; -pub(crate) const PRESERVED_NAMESPACE_DEFERRAL: &str = "preserved_namespace"; -pub(crate) const NORMALIZATION_DEFERRAL: &str = "normalization"; /// Python's message when `validate` rejects SQL without naming an error. pub(crate) const INVALID_SQL: &str = "invalid SQL"; -pub(crate) const UNSUPPORTED_DIALECT_DEFERRAL: &str = "unsupported_dialect"; +/// The `context_label`s Python's target expansions name in their errors. +pub(crate) const MANAGED_SOURCE_TARGET_LABEL: &str = "managed source target"; +pub(crate) const DEFAULT_SEED_DATABASE_LABEL: &str = "default seed database"; +pub(crate) const DEFAULT_SEED_SCHEMA_LABEL: &str = "default seed schema"; +pub(crate) const TARGET_DATABASE_LABEL: &str = "target database"; +pub(crate) const TARGET_SCHEMA_LABEL: &str = "target schema"; +pub(crate) const DATABASE_DIMENSION: &str = "database"; +pub(crate) const SCHEMA_DIMENSION: &str = "schema"; +/// An `ENV:` name the request did not look up, which the request builder's scan always finds. +pub(crate) const UNLISTED_ENVIRONMENT: &str = + "a target template read an environment variable the request did not list"; +/// `AttachedAuditTargetKind`'s name in the `ValueError` its constructor raises. +pub(crate) const AUDIT_TARGET_KIND_ENUM: &str = "AttachedAuditTargetKind"; diff --git a/crates/sqlbuild-analysis/src/assembly/project/errors.rs b/crates/sqlbuild-analysis/src/assembly/project/errors.rs new file mode 100644 index 0000000000..69d4586459 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/project/errors.rs @@ -0,0 +1,17 @@ +//! The errors Python's resource assembly raises. + +/// The error Python's resource assembly raises. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AssemblyError { + /// `CompileInputError(message, help=help)`. + CompileInput { + message: String, + help: Option, + }, + /// `ValueError(message)`. + Value(String), + /// The exception the request holds at this index, raised as is. + Raise(usize), + /// An internal native failure. + Internal(String), +} diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs index 87bac39655..4b04fed11d 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/assemble_project_resources.rs @@ -1,66 +1,11 @@ -//! Python's resource assembly facts for one project, or why Python must assemble it. - -use std::cell::RefCell; +//! Python's resource assembly facts for one project, up to the resource Python raises at. use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; -use crate::assembly::project::_helpers::deps::{audit_deps, reference_deps}; -use crate::assembly::project::_helpers::syntax::syntax_valid; -use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; -use crate::assembly::project::_helpers::templates::TemplateInputs; -use crate::assembly::project::models::{ModelFacts, ProjectRequest, ProjectResources}; -use crate::assembly::project::types::Fact; +use crate::assembly::project::_helpers::resources::project_resources; +use crate::assembly::project::models::{ProjectRequest, ProjectResources}; -/// Each resource's dependencies and namespace; Err is a deferral kind or a native failure. +/// Each resource's facts up to the first resource Python raises at; Err is a native failure. pub fn assemble_project_resources(request: &ProjectRequest) -> Result { - catch_compiler_panic(|| resources(request)) -} - -fn resources(request: &ProjectRequest) -> Fact { - let model_syntax_valid: Vec = request - .models - .iter() - .map(|model| valid_syntax(model, &request.dialect)) - .collect(); - let target = request.target.as_ref(); - let inputs = TemplateInputs { - variables: &request.variables, - environment: &request.environment, - reads: RefCell::new(Vec::new()), - }; - let sources = request - .sources - .iter() - .map(|source| managed_source(source, target, &inputs)) - .collect::>()?; - let seeds = request - .seeds - .iter() - .map(|seed| seed_target(seed, &request.defaults, target, &inputs)) - .collect::>()?; - Ok(ProjectResources { - model_syntax_valid, - model_deps: request - .models - .iter() - .map(|model| reference_deps(&model.references)) - .collect(), - sources, - seeds, - function_deps: request - .functions - .iter() - .map(|function| reference_deps(&function.references)) - .collect(), - audit_deps: request.audits.iter().map(audit_deps).collect::>()?, - reads: inputs.reads.into_inner(), - }) -} - -/// Whether Python's model and hook syntax validation passes. -fn valid_syntax(model: &ModelFacts, dialect: &str) -> bool { - model - .syntax_checks - .iter() - .all(|check| syntax_valid(check, dialect) == Ok(true)) + catch_compiler_panic(|| Ok(project_resources(request))) } diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs index bda9d3e5ff..2e9c2203a8 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs @@ -3,14 +3,17 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::project::_helpers::syntax::syntax_valid; -use crate::assembly::project::models::SyntaxCheck; +use crate::assembly::project::models::{SyntaxCheck, SyntaxFailure}; -/// Whether every check parses; Err is a deferral kind or a native failure. -pub fn check_sql_syntax(dialect: &str, checks: &[SyntaxCheck]) -> Result { - catch_compiler_panic(|| all_valid(dialect, checks)) +/// Whether every check parses, stopping at the first rejection or `ValueError`; Err is a panic. +pub fn check_sql_syntax( + dialect: &str, + checks: &[SyntaxCheck], +) -> Result, String> { + catch_compiler_panic(|| Ok(all_valid(dialect, checks))) } -fn all_valid(dialect: &str, checks: &[SyntaxCheck]) -> Result { +fn all_valid(dialect: &str, checks: &[SyntaxCheck]) -> Result { for check in checks { if !syntax_valid(check, dialect)? { return Ok(false); diff --git a/crates/sqlbuild-analysis/src/assembly/project/mod.rs b/crates/sqlbuild-analysis/src/assembly/project/mod.rs index 4439eb2731..9363ac4cab 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod _helpers; pub(crate) mod constants; +pub mod errors; pub mod main; pub mod models; pub mod types; diff --git a/crates/sqlbuild-analysis/src/assembly/project/models.rs b/crates/sqlbuild-analysis/src/assembly/project/models.rs index 0d71ab5107..4dea56fc1f 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/models.rs @@ -3,6 +3,7 @@ use sqlbuild_model_config::templates::models::Scalar; use crate::assembly::analysis_session::types::Pairs; +use crate::assembly::project::errors::AssemblyError; use crate::assembly::project::types::ObjectKey; /// One SQL reference a resource makes: Python's `CompileSqlReference`. @@ -14,11 +15,45 @@ pub struct Reference { pub package: Option, } -/// One project variable, as a scalar Python renders with `str()`, or a value only Python renders. +/// One project variable: a scalar, or another Python value described by its Python renderings. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Variable { Scalar(Scalar), - Unsupported, + Opaque(OpaqueVariable), +} + +/// A variable value only Python renders: its `str()` and `render_project_var_text` outcome. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OpaqueVariable { + /// `str(value)`: a whole-template result and the text the resolver compares. + pub text: String, + pub render: OpaqueRender, +} + +/// `render_project_var_text(value=value, label=label)` for any label. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OpaqueRender { + Text(String), + /// The `ValueError` message after the label. + Rejected(String), + /// Another exception, which Python raises; the index of the exception the request holds. + Raises(usize), +} + +/// Which resource list a failure belongs to. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResourceSlot { + Source, + Seed, + Audit, +} + +/// The first resource whose assembly raises, in Python's assembly order. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResourceFailure { + pub slot: ResourceSlot, + pub index: usize, + pub error: AssemblyError, } /// One environment or context lookup a template made, which Python records for compile reuse. @@ -158,4 +193,6 @@ pub struct ProjectResources { pub function_deps: Vec>, pub audit_deps: Vec>, pub reads: Vec, + /// The resource Python raises at; the lists stop before it and later resources are absent. + pub failure: Option, } diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs index 10e8587212..d876b617ae 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs @@ -8,8 +8,8 @@ use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; use crate::assembly::project::_helpers::templates::TemplateInputs; use crate::assembly::project::main::check_sql_syntax::check_sql_syntax; use crate::assembly::project::models::{ - AuditFacts, InputRead, Namespace, Reference, SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, - TargetNamespace, Variable, + AuditFacts, InputRead, Namespace, OpaqueRender, OpaqueVariable, Reference, SeedDefaults, + SeedFacts, SourceFacts, SyntaxCheck, TargetNamespace, Variable, }; use crate::assembly::project::tests::test_types::{ExpectedNamespace, SeedSpec}; use crate::assembly::project::types::ObjectKey; @@ -125,7 +125,7 @@ pub(crate) fn all_valid(sqls: &[&str]) -> Option { placeholders: vec![("x".to_owned(), "1".to_owned())], }) .collect(); - check_sql_syntax("generic", &checks).ok() + check_sql_syntax("generic", &checks).ok()?.ok() } pub(crate) fn namespace(expected: ExpectedNamespace) -> Namespace { @@ -175,6 +175,16 @@ fn variables() -> Vec<(String, Variable)> { Variable::Scalar(Scalar::Text("3".to_owned())), ), ("flag".to_owned(), Variable::Scalar(Scalar::Bool(true))), - ("tags".to_owned(), Variable::Unsupported), + ( + "tags".to_owned(), + Variable::Opaque(OpaqueVariable { + text: "['daily']".to_owned(), + render: OpaqueRender::Rejected( + " is an array and cannot be interpolated as text: [\"daily\"]. Use a macro \ + to consume structured vars." + .to_owned(), + ), + }), + ), ] } diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs index 5ddb9767b5..d9549857eb 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs @@ -59,7 +59,7 @@ fn given_references_when_deriving_deps_then_matches_python_keys() { } #[test] -fn given_seeds_when_resolving_targets_then_matches_python_or_defers() { +fn given_seeds_when_resolving_targets_then_matches_python_or_raises() { let test_cases = [ SeedTestCase { description: "defaults and the seed's templates read variables and updated context", @@ -116,17 +116,24 @@ fn given_seeds_when_resolving_targets_then_matches_python_or_defers() { expected_namespace: None, }, SeedTestCase { - description: "environment reads stay with Python", + description: "an environment name the request did not list is an internal failure", seed: ("countries", None, Some("${ENV:SEED_SCHEMA}")), defaults: (None, None, None, None), target: None, expected_namespace: None, }, SeedTestCase { - description: "a variable only Python renders", + description: "a whole-template list variable is Python's str of the list", seed: ("countries", None, Some("${tags}")), defaults: (None, None, None, None), target: None, + expected_namespace: Some((None, Some("['daily']"), None, Some("['daily']"))), + }, + SeedTestCase { + description: "a list variable inside text raises Python's interpolation error", + seed: ("countries", None, Some("s_${tags}")), + defaults: (None, None, None, None), + target: None, expected_namespace: None, }, ]; @@ -181,7 +188,7 @@ fn given_sources_when_resolving_managed_targets_then_matches_python() { } #[test] -fn given_model_sql_when_validating_syntax_then_matches_python_or_defers() { +fn given_model_sql_when_validating_syntax_then_matches_python_or_raises() { let test_cases = [ SyntaxTestCase { dialect: "duckdb", @@ -261,7 +268,7 @@ fn given_environment_templates_when_resolving_seeds_then_matches_python_values_a expected_reads: vec![InputRead::Environment("SQB_UNSET_SCHEMA".to_owned())], }, EnvironmentTestCase { - description: "a name Python did not resolve stays with Python", + description: "a name the request did not list is an internal failure", seed: ( "countries", None, @@ -292,7 +299,7 @@ fn given_environment_templates_when_resolving_seeds_then_matches_python_values_a } #[test] -fn given_sql_batches_when_checking_syntax_then_stops_at_python_first_rejection_or_defers() { +fn given_sql_batches_when_checking_syntax_then_stops_at_python_first_rejection_or_raise() { let test_cases = [ SyntaxBatchTestCase { description: "every statement parses", diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs index ca67d431c2..adfbe725ba 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs @@ -53,7 +53,7 @@ pub(crate) struct SyntaxTestCase { pub(crate) dialect: &'static str, pub(crate) sql: &'static str, pub(crate) placeholders: &'static [(&'static str, &'static str)], - /// None where native defers to Python. + /// None where Python raises. pub(crate) expected_valid: Option, } @@ -77,6 +77,6 @@ pub(crate) struct EnvironmentTestCase { pub(crate) struct SyntaxBatchTestCase { pub(crate) description: &'static str, pub(crate) sqls: &'static [&'static str], - /// None where native defers to Python. + /// None where Python raises. pub(crate) expected_valid: Option, } diff --git a/crates/sqlbuild-analysis/src/assembly/project/types.rs b/crates/sqlbuild-analysis/src/assembly/project/types.rs index 8d39093007..981e162728 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/types.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/types.rs @@ -1,7 +1,9 @@ //! Project assembly key and result aliases. -/// One step's result, or the deferral kind Python must assemble the project for. -pub(crate) type Fact = Result; +use crate::assembly::project::errors::AssemblyError; + +/// One step's result, or the error Python's assembly raises there. +pub(crate) type Fact = Result; /// A `CompiledObjectKey`: `(resource type, name)`. pub type ObjectKey = (String, String); diff --git a/crates/sqlbuild-core/src/text/main/mod.rs b/crates/sqlbuild-core/src/text/main/mod.rs index 8104f495de..94aabc44f6 100644 --- a/crates/sqlbuild-core/src/text/main/mod.rs +++ b/crates/sqlbuild-core/src/text/main/mod.rs @@ -10,6 +10,7 @@ pub mod python_casefold; pub mod python_cleandoc; pub mod python_decimal_value; pub mod python_ignorecase_key; +pub mod python_str_repr; pub mod python_strip; pub mod python_text; pub mod python_upper; diff --git a/crates/sqlbuild-core/src/text/main/python_str_repr.rs b/crates/sqlbuild-core/src/text/main/python_str_repr.rs new file mode 100644 index 0000000000..092f9de9ed --- /dev/null +++ b/crates/sqlbuild-core/src/text/main/python_str_repr.rs @@ -0,0 +1,33 @@ +//! Python's `repr` of a `str`. + +/// Python's `repr` of a string, or `None` when it holds text whose escaping needs Unicode tables. +pub fn python_str_repr(value: &str) -> Option { + if !value.is_ascii() { + return None; + } + let quote: char = if value.contains('\'') && !value.contains('"') { + '"' + } else { + '\'' + }; + let mut text: String = String::with_capacity(value.len() + 2); + text.push(quote); + for character in value.chars() { + match character { + '\\' => text.push_str("\\\\"), + '\t' => text.push_str("\\t"), + '\n' => text.push_str("\\n"), + '\r' => text.push_str("\\r"), + _ if character == quote => { + text.push('\\'); + text.push(character); + } + _ if character < ' ' || character == '\x7f' => { + text.push_str(&format!("\\x{:02x}", u32::from(character))); + } + _ => text.push(character), + } + } + text.push(quote); + Some(text) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index 94f026666a..9e1646f00c 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -5,18 +5,18 @@ use std::collections::HashMap; use pyo3::exceptions::PyValueError; use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::types::{PyString, PyStringMethods}; -use pyo3::{pyfunction, wrap_pyfunction}; +use pyo3::{PyErr, pyfunction, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::types::Pairs; +use sqlbuild_analysis::assembly::project::errors::AssemblyError; use sqlbuild_analysis::assembly::project::main::assemble_project_resources::assemble_project_resources; use sqlbuild_analysis::assembly::project::main::check_sql_syntax::check_sql_syntax; use sqlbuild_analysis::assembly::project::main::sql_syntax_error::sql_syntax_error as check_syntax_error; use sqlbuild_analysis::assembly::project::models::{ - AuditFacts, FunctionFacts, InputRead, ModelFacts, Namespace, ProjectRequest, ProjectResources, - Reference, SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, SyntaxFailure, SyntaxMode, - TargetNamespace, Variable, + AuditFacts, FunctionFacts, InputRead, ModelFacts, Namespace, OpaqueRender, OpaqueVariable, + ProjectRequest, ProjectResources, Reference, ResourceFailure, ResourceSlot, SeedDefaults, + SeedFacts, SourceFacts, SyntaxCheck, SyntaxFailure, SyntaxMode, TargetNamespace, Variable, }; use sqlbuild_analysis::assembly::project::types::ObjectKey; -use sqlbuild_core::panics::main::is_compiler_panic::is_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; use sqlbuild_model_config::templates::models::Scalar; @@ -29,6 +29,16 @@ const SYNTAX_CHECK_CONTEXT: &str = "native SQL syntax check"; const TEXT_VARIABLE: &str = "text"; const BOOLEAN_VARIABLE: &str = "bool"; const NONE_VARIABLE: &str = "none"; +const OPAQUE_VARIABLE: &str = "opaque"; +const RENDERED_TEXT: &str = "text"; +const RENDER_REJECTED: &str = "rejected"; +const RENDER_RAISES: &str = "raises"; +const SOURCE_SLOT: &str = "source"; +const SEED_SLOT: &str = "seed"; +const AUDIT_SLOT: &str = "audit"; +const COMPILE_INPUT_ERROR: &str = "compile_input"; +const VALUE_ERROR: &str = "value"; +const HELD_ERROR: &str = "held"; const TRUE_TEXT: &str = "1"; const ENVIRONMENT_READ: &str = "env"; const CONTEXT_READ: &str = "ctx"; @@ -44,8 +54,17 @@ type DefaultsRow = ( Option, Option, ); -/// `(name, kind, text)`; kind `text` (also exact ints), `bool`, `none` or anything else. -type VariableRow = (String, String, String); +/// `(name, kind, text, render)`: kind `text`, `bool`, `none` or `opaque` with a render pair. +type VariableRow = (String, String, String, Option<(String, String)>); +/// `(resource list, index, error class, message, help, held exception index)`. +type FailureRow = ( + &'static str, + usize, + &'static str, + String, + Option, + Option, +); /// `(name, directory, tags, references, [(sql, placeholders)])`. type ModelRow = ( String, @@ -91,23 +110,17 @@ type ResourcesRow = ( Vec>, Vec<(&'static str, String)>, Vec, + Option, ); -/// The project's resource facts, or None with the reason Python must assemble it. +/// The project's resource facts up to the resource Python's assembly raises at. #[pyfunction] -fn assemble_project_resource_facts( - py: Python<'_>, - request: RequestRow, -) -> PyResult<(Option, Option)> { - let request: ProjectRequest = project_request(request); - match py.compiler_detach(|| assemble_project_resources(&request)) { - Ok(resources) => Ok((Some(resources_row(resources)), None)), - Err(reason) if is_compiler_panic(&reason) => Err(compiler_error(native_failure( - PROJECT_ASSEMBLY_CONTEXT, - &reason, - ))), - Err(reason) => Ok((None, Some(reason))), - } +fn assemble_project_resource_facts(py: Python<'_>, request: RequestRow) -> PyResult { + let request: ProjectRequest = project_request(request)?; + let resources: ProjectResources = py + .compiler_detach(|| assemble_project_resources(&request)) + .map_err(|reason| compiler_error(native_failure(PROJECT_ASSEMBLY_CONTEXT, &reason)))?; + resources_row(resources) } /// Python's syntax error message for one SQL string, None where it parses. @@ -134,36 +147,36 @@ fn sql_syntax_error( }; py.compiler_detach(|| Ok(check_syntax_error(&check, &dialect, mode))) .map_err(compiler_error)? - .map_err(|failure| match failure { - SyntaxFailure::Normalization(message) => PyValueError::new_err(message), - SyntaxFailure::UnknownDialect(name) => { - PyValueError::new_err(format!("Unknown dialect: {name}")) - } - }) + .map_err(syntax_value_error) +} + +/// The `ValueError` Python's syntax validation raises before parsing. +fn syntax_value_error(failure: SyntaxFailure) -> PyErr { + match failure { + SyntaxFailure::Normalization(message) => PyValueError::new_err(message), + SyntaxFailure::UnknownDialect(name) => { + PyValueError::new_err(format!("Unknown dialect: {name}")) + } + } } -/// Whether every `(sql, placeholders)` parses, or None with the reason Python must check it. +/// Whether every `(sql, placeholders)` parses, raising the `ValueError` Python's validation raises. #[pyfunction] fn check_native_sql_syntax( py: Python<'_>, request: (String, Vec<(String, Pairs)>), -) -> PyResult<(Option, Option)> { +) -> PyResult { let (dialect, rows) = request; let checks: Vec = rows .into_iter() .map(|(sql, placeholders)| SyntaxCheck { sql, placeholders }) .collect(); - match py.compiler_detach(|| check_sql_syntax(&dialect, &checks)) { - Ok(valid) => Ok((Some(valid), None)), - Err(reason) if is_compiler_panic(&reason) => Err(compiler_error(native_failure( - SYNTAX_CHECK_CONTEXT, - &reason, - ))), - Err(reason) => Ok((None, Some(reason))), - } + py.compiler_detach(|| check_sql_syntax(&dialect, &checks)) + .map_err(|reason| compiler_error(native_failure(SYNTAX_CHECK_CONTEXT, &reason)))? + .map_err(syntax_value_error) } -fn project_request(request: RequestRow) -> ProjectRequest { +fn project_request(request: RequestRow) -> PyResult { let ( dialect, target, @@ -177,7 +190,7 @@ fn project_request(request: RequestRow) -> ProjectRequest { audits, ) = request; let (database, schema, seed_database, seed_schema) = defaults; - ProjectRequest { + Ok(ProjectRequest { dialect, target: target.map(|(database, schema, loader_schema)| TargetNamespace { database, @@ -190,7 +203,10 @@ fn project_request(request: RequestRow) -> ProjectRequest { seed_database, seed_schema, }, - variables: variables.into_iter().map(variable).collect(), + variables: variables + .into_iter() + .map(variable) + .collect::>()?, environment, models: models.into_iter().map(model).collect(), sources: sources @@ -227,18 +243,44 @@ fn project_request(request: RequestRow) -> ProjectRequest { attached, }) .collect(), - } + }) } -fn variable(row: VariableRow) -> (String, Variable) { - let (name, kind, text) = row; - let value: Variable = match kind.as_str() { - TEXT_VARIABLE => Variable::Scalar(Scalar::Text(text)), - BOOLEAN_VARIABLE => Variable::Scalar(Scalar::Bool(text == TRUE_TEXT)), - NONE_VARIABLE => Variable::Scalar(Scalar::Null), - _ => Variable::Unsupported, +fn variable(row: VariableRow) -> PyResult<(String, Variable)> { + let (name, kind, text, render) = row; + let value: Variable = match (kind.as_str(), render) { + (TEXT_VARIABLE, _) => Variable::Scalar(Scalar::Text(text)), + (BOOLEAN_VARIABLE, _) => Variable::Scalar(Scalar::Bool(text == TRUE_TEXT)), + (NONE_VARIABLE, _) => Variable::Scalar(Scalar::Null), + (OPAQUE_VARIABLE, Some((render_kind, payload))) => Variable::Opaque(OpaqueVariable { + text, + render: opaque_render(&render_kind, payload)?, + }), + _ => { + return Err(compiler_error(native_failure( + PROJECT_ASSEMBLY_CONTEXT, + &format!("an unknown variable kind '{kind}'"), + ))); + } }; - (name, value) + Ok((name, value)) +} + +fn opaque_render(kind: &str, payload: String) -> PyResult { + match kind { + RENDERED_TEXT => Ok(OpaqueRender::Text(payload)), + RENDER_REJECTED => Ok(OpaqueRender::Rejected(payload)), + RENDER_RAISES => payload.parse().map(OpaqueRender::Raises).map_err(|_| { + compiler_error(native_failure( + PROJECT_ASSEMBLY_CONTEXT, + "an opaque variable's held exception index is not a number", + )) + }), + _ => Err(compiler_error(native_failure( + PROJECT_ASSEMBLY_CONTEXT, + &format!("an unknown variable rendering '{kind}'"), + ))), + } } fn model(row: ModelRow) -> ModelFacts { @@ -265,8 +307,9 @@ fn references(rows: Vec) -> Vec { .collect() } -fn resources_row(resources: ProjectResources) -> ResourcesRow { - ( +fn resources_row(resources: ProjectResources) -> PyResult { + let failure: Option = resources.failure.map(failure_row).transpose()?; + Ok(( resources.model_deps, resources.sources, resources.seeds.into_iter().map(namespace_row).collect(), @@ -274,7 +317,42 @@ fn resources_row(resources: ProjectResources) -> ResourcesRow { resources.audit_deps, resources.reads.into_iter().map(read_row).collect(), resources.model_syntax_valid, - ) + failure, + )) +} + +/// `(resource list, index, error class, message, help, held exception index)`. +fn failure_row(failure: ResourceFailure) -> PyResult { + let slot: &'static str = match failure.slot { + ResourceSlot::Source => SOURCE_SLOT, + ResourceSlot::Seed => SEED_SLOT, + ResourceSlot::Audit => AUDIT_SLOT, + }; + Ok(match failure.error { + AssemblyError::CompileInput { message, help } => ( + slot, + failure.index, + COMPILE_INPUT_ERROR, + message, + help, + None, + ), + AssemblyError::Value(message) => (slot, failure.index, VALUE_ERROR, message, None, None), + AssemblyError::Raise(held) => ( + slot, + failure.index, + HELD_ERROR, + String::new(), + None, + Some(held), + ), + AssemblyError::Internal(reason) => { + return Err(compiler_error(native_failure( + PROJECT_ASSEMBLY_CONTEXT, + &reason, + ))); + } + }) } fn read_row(read: InputRead) -> (&'static str, String) { diff --git a/crates/sqlbuild-scopes/src/scope_index/_helpers/identities.rs b/crates/sqlbuild-scopes/src/scope_index/_helpers/identities.rs index 12d4640057..953352e6d7 100644 --- a/crates/sqlbuild-scopes/src/scope_index/_helpers/identities.rs +++ b/crates/sqlbuild-scopes/src/scope_index/_helpers/identities.rs @@ -3,6 +3,7 @@ use crate::scope_index::models::{ DeclarationIdentity, GrantRecord, GrantThrough, ResourceIdentity, ScopeIndex, }; +use sqlbuild_core::text::main::python_str_repr::python_str_repr; /// Python's `format_identity` for a resource. pub(crate) fn resource_text(identity: &ResourceIdentity) -> String { @@ -33,38 +34,6 @@ pub(crate) fn grant_through_text(index: &ScopeIndex, grant: &GrantRecord) -> Str } } -/// Python's `repr` of a string, or `None` when it holds text whose escaping needs Unicode tables. -pub(crate) fn python_str_repr(value: &str) -> Option { - if !value.is_ascii() { - return None; - } - let quote: char = if value.contains('\'') && !value.contains('"') { - '"' - } else { - '\'' - }; - let mut text: String = String::with_capacity(value.len() + 2); - text.push(quote); - for character in value.chars() { - match character { - '\\' => text.push_str("\\\\"), - '\t' => text.push_str("\\t"), - '\n' => text.push_str("\\n"), - '\r' => text.push_str("\\r"), - _ if character == quote => { - text.push('\\'); - text.push(character); - } - _ if character < ' ' || character == '\x7f' => { - text.push_str(&format!("\\x{:02x}", u32::from(character))); - } - _ => text.push(character), - } - } - text.push(quote); - Some(text) -} - /// Python's dataclass `repr` of a `ResourceIdentity`. pub(crate) fn resource_repr(identity: &ResourceIdentity) -> Option { Some(format!( diff --git a/crates/sqlbuild-scopes/src/scope_index/main/scope_lookup.rs b/crates/sqlbuild-scopes/src/scope_index/main/scope_lookup.rs index 9c96d417e2..616304228a 100644 --- a/crates/sqlbuild-scopes/src/scope_index/main/scope_lookup.rs +++ b/crates/sqlbuild-scopes/src/scope_index/main/scope_lookup.rs @@ -1,8 +1,7 @@ //! The groupings Python's `build_lookup` derives from an index and its relationship grants. use crate::scope_index::_helpers::identities::{ - declaration_repr, declaration_text, grant_through_text, python_str_repr, resource_repr, - resource_text, + declaration_repr, declaration_text, grant_through_text, resource_repr, resource_text, }; use crate::scope_index::_helpers::ordering::{keyed_groups, sorted_by_key}; use crate::scope_index::_helpers::paths::normalize_path; @@ -10,6 +9,7 @@ use crate::scope_index::_helpers::visibility::VisibilityIndex; use crate::scope_index::models::{ DeclarationIdentity, GrantRecord, ScopeDeferral, ScopeIndex, ScopeLookupGroups, }; +use sqlbuild_core::text::main::python_str_repr::python_str_repr; /// Every lookup grouping of `index` once `grants` are attached to it. pub fn scope_lookup_groups( diff --git a/crates/sqlbuild-scopes/src/scope_index/tests/test_scope_lookup.rs b/crates/sqlbuild-scopes/src/scope_index/tests/test_scope_lookup.rs index 3265fb915e..b89ccd9df8 100644 --- a/crates/sqlbuild-scopes/src/scope_index/tests/test_scope_lookup.rs +++ b/crates/sqlbuild-scopes/src/scope_index/tests/test_scope_lookup.rs @@ -1,6 +1,6 @@ -use crate::scope_index::_helpers::identities::python_str_repr; use crate::scope_index::tests::helpers::{resource_groups, visibility_texts}; use crate::scope_index::tests::test_types::{LookupTestCase, ReprTestCase}; +use sqlbuild_core::text::main::python_str_repr::python_str_repr; #[test] fn given_identity_keys_when_grouping_then_groups_follow_python_repr_order() { diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 482b67a1c8..408b883a8a 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -6,20 +6,6 @@ seed_start = 0 seeds = 12 -[[entry]] -engine = "native" -stage = "analysis" -site = "project_assembly" -kind = "syntax_error" -counts = { failure = 3 } - -[[entry]] -engine = "native" -stage = "analysis" -site = "project_assembly" -kind = "unsupported_dialect" -counts = { failure = 1 } - [[entry]] engine = "native" stage = "macro_calls" @@ -27,13 +13,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native" -stage = "project_assembly" -site = "project_assembly.assembly" -kind = "deferred" -counts = { failure = 4 } - [[entry]] engine = "native" stage = "sql_test_glue" @@ -41,20 +20,6 @@ site = "sql_test_glue.macro_mock_queries" kind = "extension_callback" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "analysis" -site = "project_assembly" -kind = "syntax_error" -counts = { failure = 3 } - -[[entry]] -engine = "native-preview" -stage = "analysis" -site = "project_assembly" -kind = "unsupported_dialect" -counts = { failure = 1 } - [[entry]] engine = "native-preview" stage = "macro_calls" @@ -62,13 +27,6 @@ site = "macro_calls.mocked_evaluation" kind = "test_mock" counts = { seed = 29 } -[[entry]] -engine = "native-preview" -stage = "project_assembly" -site = "project_assembly.assembly" -kind = "deferred" -counts = { failure = 4 } - [[entry]] engine = "native-preview" stage = "sql_test_glue" diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index f33f671ee5..df99137fce 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -665,13 +665,13 @@ def sql_syntax_error( ) -> str | None: ... def check_native_sql_syntax( request: tuple[str, list[tuple[str, list[tuple[str, str]]]]], / -) -> tuple[bool | None, str | None]: ... +) -> bool: ... def assemble_project_resource_facts( request: tuple[ str, tuple[str | None, str | None, str | None] | None, tuple[str | None, str | None, str | None, str | None], - list[tuple[str, str, str]], + list[tuple[str, str, str, tuple[str, str] | None]], list[tuple[str, str | None]], list[ tuple[ @@ -689,20 +689,15 @@ def assemble_project_resource_facts( ], /, ) -> tuple[ - tuple[ - list[list[tuple[str, str]]], - list[tuple[str | None, str | None] | None], - list[tuple[str | None, str | None, str | None, str | None]], - list[list[tuple[str, str]]], - list[list[tuple[str, str]]], - list[tuple[str, str]], - list[bool], - ] - | None, - str | None, + list[list[tuple[str, str]]], + list[tuple[str | None, str | None] | None], + list[tuple[str | None, str | None, str | None, str | None]], + list[list[tuple[str, str]]], + list[list[tuple[str, str]]], + list[tuple[str, str]], + list[bool], + tuple[str, int, str, str, str | None, int | None] | None, ]: ... - -# Internal oracle hooks for tests that compare native foundations with Python; not an API. def _oracle_json_dumps(dialect_json: str, value_json: str) -> str: ... def _oracle_text_positions( data: bytes, byte_offsets: list[int] diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 90cf2c8227..711b6e09ff 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -44,15 +44,6 @@ ) from sqlbuild.compiler.compile._helpers.assembly.targets import ( build_model_relation_target, - build_seed_relation_target, -) -from sqlbuild.compiler.compile._helpers.config.namespace_validation import ( - validate_preserved_logical_namespace, -) -from sqlbuild.compiler.compile._helpers.deps.dependencies import ( - audit_scope_deps, - function_build_deps, - model_build_deps, ) from sqlbuild.compiler.compile._helpers.diagnostics.recovery import complete_semantic_diagnostics from sqlbuild.compiler.compile._helpers.diagnostics.scope import report_scope_index_errors @@ -76,7 +67,6 @@ expand_sql_macros, find_macro_call_names, ) -from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data from sqlbuild.compiler.compile.main._scope_index_with_compile_usages import ( scope_index_with_compile_usages, ) @@ -121,6 +111,8 @@ from sqlbuild.compiler.planner.types import ContractPolicy from sqlbuild.compiler.profiling.main._record_cpu import record_compile_cpu_timing from sqlbuild.compiler.profiling.main.record import record_compile_timing +from sqlbuild.compiler.project_assembly.constants import AUDIT_SLOT, SEED_SLOT, SOURCE_SLOT +from sqlbuild.compiler.project_assembly.main._native_resource import native_resource from sqlbuild.compiler.project_assembly.models import ( NativeModelFacts, NativeProjectFacts, @@ -145,11 +137,9 @@ resolve_effective_scenario_config, ) from sqlbuild.spec.contracts.models import ( - DefaultsConfig, SchemaDynamicColumnFamily, SourceEntry, SourceLocation, - TargetConfig, ) @@ -173,9 +163,6 @@ def assemble_compiled_project( sql_analysis_enabled: bool = ( inputs.effective_settings.sql_analysis and not skip_column_inference ) - seed_names: frozenset[str] = frozenset( - seed_input.schema_entry.name for seed_input in inputs.seed_inputs - ) column_nullability_by_table: dict[str, dict[str, InferredNullability]] = ( build_column_nullability_by_table(inputs) ) @@ -307,7 +294,7 @@ def assemble_compiled_project( dynamic_families_by_table=dynamic_families_by_table, ) ) - native: NativeProjectFacts | None = project_facts_by_engine( + native: NativeProjectFacts = project_facts_by_engine( inputs=inputs, dialect=profile.sql_analysis_dialect, analysis_model_names=analysis_model_names, @@ -318,7 +305,7 @@ def assemble_compiled_project( column_nullability_by_table=column_nullability_by_table, dynamic_families_by_table=dynamic_families_by_table, ) - resources: NativeProjectResources | None = native.resources if native else None + resources: NativeProjectResources = native.resources scope_index: ScopeIndex = scope_index_with_compile_usages(inputs=inputs) report_scope_index_errors(index=scope_index) effective_target_values: dict[str, object] = resolve_early_model_templates( @@ -371,7 +358,7 @@ def assemble_compiled_project( sql_validation_enabled=( analysis_model_names is None or _model_name(model_input) in analysis_model_names ), - native_model=native.models[index] if native else None, + native_model=native.models[index], column_nullability_by_table=column_nullability_by_table, column_types_by_table=column_types_by_table, dynamic_contract_analysis_inputs=dynamic_contract_analysis_inputs, @@ -384,34 +371,43 @@ def assemble_compiled_project( sources=tuple( _assemble_compiled_source( source_input=source_input, - target_config=inputs.effective_target, - effective_vars=inputs.effective_vars, - native_entry=resources.source_entries[index] if resources else None, + native_entry=native_resource( + resources=resources, + slot=SOURCE_SLOT, + items=resources.source_entries, + index=index, + ), ) for index, source_input in enumerate(inputs.source_inputs) ), seeds=tuple( _assemble_compiled_seed( seed_input=seed_input, - defaults=inputs.project_config.defaults, - target_config=inputs.effective_target, - effective_vars=inputs.effective_vars, - native_destination=resources.seed_destinations[index] if resources else None, + native_destination=native_resource( + resources=resources, + slot=SEED_SLOT, + items=resources.seed_destinations, + index=index, + ), ) for index, seed_input in enumerate(inputs.seed_inputs) ), functions=tuple( _assemble_compiled_function( function_input=function_input, - seed_names=seed_names, - native_deps=resources.function_deps[index] if resources else None, + native_deps=resources.function_deps[index], ) for index, function_input in enumerate(inputs.sql_function_inputs) ), audits=tuple( _assemble_compiled_audit( audit_input=audit_input, - native_scope_deps=resources.audit_scope_deps[index] if resources else None, + native_scope_deps=native_resource( + resources=resources, + slot=AUDIT_SLOT, + items=resources.audit_scope_deps, + index=index, + ), ) for index, audit_input in enumerate(inputs.audit_inputs) ), @@ -475,7 +471,7 @@ def _assemble_compiled_model( model_input: CompileModelInput, sql_analysis_enabled: bool, sql_validation_enabled: bool = True, - native_model: NativeModelFacts | None = None, + native_model: NativeModelFacts, column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, column_types_by_table: dict[str, dict[str, str]] | None = None, dynamic_contract_analysis_inputs: _DynamicContractAnalysisInputs | None = None, @@ -484,8 +480,7 @@ def _assemble_compiled_model( allow_compact_analysis: bool = False, ) -> CompiledModel: model_name: str = model_input.model_file.file_path.stem - native_deps: tuple[CompiledObjectKey, ...] | None = native_model.deps if native_model else None - syntax_validated: bool = native_model is not None and native_model.syntax_valid + syntax_validated: bool = native_model.syntax_valid profile: ExpressionInferenceProfile = inference_profile or ExpressionInferenceProfile() analysis_query_sql: str = cursor_intrinsics_analysis_sql( sql=model_input.query_sql, @@ -550,19 +545,13 @@ def _assemble_compiled_model( sql_analysis.dynamic_column_contract if sql_analysis is not None and sql_analysis.dynamic_column_contract is not None else native_model.dynamic_contract - if native_model is not None - else None ) if dynamic_column_contract is not None and dynamic_column_contract.output_proven: inferred_columns = dynamic_column_contract.fixed_columns fast_lineage_has_star = True return CompiledModel( key=CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name=model_name), - deps=( - native_deps - if native_deps is not None - else model_build_deps(references=model_input.references) - ), + deps=native_model.deps, name=model_name, relative_path=model_input.model_file.relative_path, query_sql=model_input.query_sql, @@ -704,103 +693,20 @@ def _binding_compiler_diagnostics( def _assemble_compiled_source( - *, - source_input: CompileSourceInput, - target_config: TargetConfig | None, - effective_vars: dict[str, object], - native_entry: SourceEntry | None = None, + *, source_input: CompileSourceInput, native_entry: SourceEntry ) -> CompiledSource: - source_entry: SourceEntry = ( - native_entry - if native_entry is not None - else _build_source_relation_entry( - source_entry=source_input.source_entry, - target_config=target_config, - effective_vars=effective_vars, - ) - ) return CompiledSource( - key=CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name=source_entry.name), + key=CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name=native_entry.name), deps=(), - name=source_entry.name, - source_entry=source_entry, + name=native_entry.name, + source_entry=native_entry, source_file=source_input.source_file, ) -def _build_source_relation_entry( - *, - source_entry: SourceEntry, - target_config: TargetConfig | None, - effective_vars: dict[str, object], -) -> SourceEntry: - if source_entry.loader is None or target_config is None: - return source_entry - loader_target_config: TargetConfig = replace( - target_config, - schema=target_config.loader_schema or target_config.schema, - ) - validate_preserved_logical_namespace( - resource_label=f"Managed source '{source_entry.name}'", - logical_database=source_entry.database, - logical_schema=source_entry.schema, - target_config=loader_target_config, - ) - return replace( - source_entry, - database=( - source_entry.database - if source_entry.database is not None - else _expand_target_value( - value=target_config.database, - effective_vars=effective_vars, - ) - ), - schema=( - source_entry.schema - if source_entry.schema is not None - else _expand_target_value( - value=target_config.loader_schema or target_config.schema, - effective_vars=effective_vars, - ) - ), - ) - - -def _expand_target_value(*, value: str | None, effective_vars: dict[str, object]) -> str | None: - if value is None: - return None - return str( - expand_template_data( - value=value, - variables=effective_vars, - context_values={}, - context_label="managed source target", - allow_context=False, - preserve_context_tokens=False, - preserve_unknown_context=False, - ) - ) - - def _assemble_compiled_seed( - *, - seed_input: CompileSeedInput, - defaults: DefaultsConfig, - target_config: TargetConfig | None, - effective_vars: dict[str, object], - native_destination: CompiledRelationLocation | None = None, + *, seed_input: CompileSeedInput, native_destination: CompiledRelationLocation ) -> CompiledSeed: - target: CompiledRelationLocation = ( - native_destination - if native_destination is not None - else build_seed_relation_target( - seed_entry=seed_input.schema_entry, - defaults=defaults, - target_config=target_config, - effective_vars=effective_vars, - ) - ) return CompiledSeed( key=CompiledObjectKey( resource_type=CompiledResourceType.SEED, name=seed_input.schema_entry.name @@ -810,15 +716,14 @@ def _assemble_compiled_seed( seed_file=seed_input.seed_file, schema_entry=seed_input.schema_entry, schema_file=seed_input.schema_file, - destination=target, + destination=native_destination, ) def _assemble_compiled_function( *, function_input: CompileSqlFunctionInput, - seed_names: frozenset[str] = frozenset(), - native_deps: tuple[CompiledObjectKey, ...] | None = None, + native_deps: tuple[CompiledObjectKey, ...], ) -> CompiledFunction: return CompiledFunction( key=CompiledObjectKey( @@ -827,11 +732,7 @@ def _assemble_compiled_function( ), name=function_input.name, ), - deps=( - native_deps - if native_deps is not None - else function_build_deps(references=function_input.references, seed_names=seed_names) - ), + deps=native_deps, name=function_input.name, relative_path=function_input.function_file.relative_path, arguments=function_input.arguments, @@ -868,7 +769,7 @@ def _assemble_compiled_function( def _assemble_compiled_audit( *, audit_input: CompileAuditInput, - native_scope_deps: tuple[CompiledObjectKey, ...] | None = None, + native_scope_deps: tuple[CompiledObjectKey, ...], ) -> CompiledAudit: audit_name: str = _resolve_audit_name(audit_input) normalized_target_kind: AttachedAuditTargetKind | None = None @@ -876,15 +777,7 @@ def _assemble_compiled_audit( normalized_target_kind = AttachedAuditTargetKind(audit_input.attached_target_kind) return CompiledAudit( key=CompiledObjectKey(resource_type=CompiledResourceType.AUDIT, name=audit_name), - scope_deps=( - native_scope_deps - if native_scope_deps is not None - else audit_scope_deps( - references=audit_input.references, - attached_target_kind=audit_input.attached_target_kind, - attached_target_name=audit_input.attached_target_name, - ) - ), + scope_deps=native_scope_deps, name=audit_name, definition_name=(audit_input.audit_block.name or audit_input.audit_file.file_path.stem), audit_file=audit_input.audit_file, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py index 28bd9e7bed..8912228100 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py @@ -2,13 +2,7 @@ from __future__ import annotations -from sqlbuild.compiler.compile._helpers.config.namespace_validation import ( - validate_preserved_logical_namespace, -) -from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data -from sqlbuild.compiler.compile.constants import PRESERVE_TARGET_VALUE from sqlbuild.compiler.compile.models import CompiledRelationLocation, CompileModelInput -from sqlbuild.spec.contracts.models import DefaultsConfig, SchemaSeedEntry, TargetConfig def build_model_relation_target( @@ -30,181 +24,3 @@ def build_model_relation_target( logical_schema=model_input.config.logical_schema, logical_database=model_input.config.logical_database, ) - - -def build_seed_relation_target( - *, - seed_entry: SchemaSeedEntry, - defaults: DefaultsConfig, - target_config: TargetConfig | None, - effective_vars: dict[str, object], -) -> CompiledRelationLocation: - """Resolve one seed's target-aware physical and logical destination.""" - - logical_namespace: tuple[str | None, str | None] = _resolve_seed_logical_namespace( - defaults=defaults, - effective_vars=effective_vars, - ) - logical_database: str | None = logical_namespace[0] - logical_schema: str | None = logical_namespace[1] - if seed_entry.database is not None: - logical_database = _expand_seed_target_value( - raw_value=seed_entry.database, - seed_name=seed_entry.name, - database=logical_database, - schema=logical_schema, - effective_vars=effective_vars, - context_label=f"seed '{seed_entry.name}' database", - ) - if seed_entry.schema is not None: - logical_schema = _expand_seed_target_value( - raw_value=seed_entry.schema, - seed_name=seed_entry.name, - database=logical_database, - schema=logical_schema, - effective_vars=effective_vars, - context_label=f"seed '{seed_entry.name}' schema", - ) - validate_preserved_logical_namespace( - resource_label=f"Seed '{seed_entry.name}'", - logical_database=logical_database, - logical_schema=logical_schema, - target_config=target_config, - ) - database, schema = _apply_seed_target_overrides( - logical_database=logical_database, - logical_schema=logical_schema, - target_config=target_config, - effective_vars=effective_vars, - ) - return CompiledRelationLocation( - database=database, - schema=schema, - name=seed_entry.name, - qualified_name=None, - logical_database=logical_database, - logical_schema=logical_schema, - ) - - -def _resolve_seed_logical_namespace( - *, - defaults: DefaultsConfig, - effective_vars: dict[str, object], -) -> tuple[str | None, str | None]: - database: str | None = _expand_seed_default_value( - raw_value=( - defaults.seed_database if defaults.seed_database is not None else defaults.database - ), - effective_vars=effective_vars, - context_label="default seed database", - ) - schema: str | None = _expand_seed_default_value( - raw_value=defaults.seed_schema if defaults.seed_schema is not None else defaults.schema, - effective_vars=effective_vars, - context_label="default seed schema", - ) - return database, schema - - -def _apply_seed_target_overrides( - *, - logical_database: str | None, - logical_schema: str | None, - target_config: TargetConfig | None, - effective_vars: dict[str, object], -) -> tuple[str | None, str | None]: - if target_config is None: - return logical_database, logical_schema - database: str | None = logical_database - schema: str | None = logical_schema - if target_config.database is not None and target_config.database != PRESERVE_TARGET_VALUE: - database = _expand_seed_environment_value( - raw_value=target_config.database, - effective_vars=effective_vars, - context_label="target database", - ) - if target_config.schema is not None and target_config.schema != PRESERVE_TARGET_VALUE: - schema = _expand_seed_environment_value( - raw_value=target_config.schema, - effective_vars=effective_vars, - context_label="target schema", - ) - return database, schema - - -def _expand_seed_default_value( - *, raw_value: str | None, effective_vars: dict[str, object], context_label: str -) -> str | None: - if raw_value is None: - return None - return _expand_seed_environment_value( - raw_value=raw_value, - effective_vars=effective_vars, - context_label=context_label, - ) - - -def _expand_seed_environment_value( - *, raw_value: str, effective_vars: dict[str, object], context_label: str -) -> str | None: - if raw_value == PRESERVE_TARGET_VALUE: - return None - return str( - expand_template_data( - value=raw_value, - variables=effective_vars, - context_values={}, - context_label=context_label, - allow_context=False, - preserve_context_tokens=False, - preserve_unknown_context=False, - ) - ) - - -def _expand_seed_target_value( - *, - raw_value: str, - seed_name: str, - database: str | None, - schema: str | None, - effective_vars: dict[str, object], - context_label: str, -) -> str | None: - if raw_value == PRESERVE_TARGET_VALUE: - return None - return str( - expand_template_data( - value=raw_value, - variables=effective_vars, - context_values={ - "model.name": seed_name, - "model.database": database, - "model.schema": schema, - "model.alias": seed_name, - "destination.database": database, - "destination.schema": schema, - "destination.table": seed_name, - "destination.qualified": _build_seed_destination_qualified_context( - database=database, - schema=schema, - name=seed_name, - ), - }, - context_label=context_label, - allow_context=True, - preserve_context_tokens=False, - preserve_unknown_context=False, - ) - ) - - -def _build_seed_destination_qualified_context( - *, database: str | None, schema: str | None, name: str -) -> str | None: - if database is not None and schema is not None: - return f"{database}.{schema}.{name}" - if schema is not None: - return f"{schema}.{name}" - return None diff --git a/src/sqlbuild/compiler/compile/_helpers/deps/dependencies.py b/src/sqlbuild/compiler/compile/_helpers/deps/dependencies.py deleted file mode 100644 index ab9bafd3d6..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/deps/dependencies.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Dependency key derivation helpers for compiled resources.""" - -from __future__ import annotations - -from collections.abc import Iterable - -from sqlbuild.compiler.compile.models import ( - CompiledObjectKey, - CompileSqlReference, -) -from sqlbuild.compiler.compile.types import ( - AttachedAuditTargetKind, - CompiledResourceType, -) -from sqlbuild.compiler.references.types import SqlReferenceKind - - -def model_build_deps( - *, - references: tuple[CompileSqlReference, ...], - seed_names: frozenset[str] = frozenset(), -) -> tuple[CompiledObjectKey, ...]: - """Return build graph dependencies implied by model SQL references.""" - - if not references: - return () - if len(references) == 1: - return (_reference_dep(reference=references[0], seed_names=seed_names),) - return _dedupe_object_keys( - _reference_dep(reference=reference, seed_names=seed_names) for reference in references - ) - - -def function_build_deps( - *, - references: tuple[CompileSqlReference, ...], - seed_names: frozenset[str] = frozenset(), -) -> tuple[CompiledObjectKey, ...]: - """Return build graph dependencies implied by function SQL references.""" - - if not references: - return () - if len(references) == 1: - return (_reference_dep(reference=references[0], seed_names=seed_names),) - return _dedupe_object_keys( - _reference_dep(reference=reference, seed_names=seed_names) for reference in references - ) - - -def audit_scope_deps( - *, - references: tuple[CompileSqlReference, ...], - attached_target_kind: AttachedAuditTargetKind | str | None, - attached_target_name: str | None, -) -> tuple[CompiledObjectKey, ...]: - """Return resources that select or gate an audit.""" - - deps: list[CompiledObjectKey] = list(model_build_deps(references=references)) - if attached_target_kind is not None and attached_target_name is not None: - deps.append( - CompiledObjectKey( - resource_type=AttachedAuditTargetKind(attached_target_kind).resource_type, - name=attached_target_name, - ) - ) - return _dedupe_object_keys(deps) - - -def sql_test_scope_deps(*, expected_model_names: tuple[str, ...]) -> tuple[CompiledObjectKey, ...]: - """Return model names that select a SQL-native unit test.""" - - if not expected_model_names: - return () - if len(expected_model_names) == 1: - return ( - CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, name=expected_model_names[0] - ), - ) - return _dedupe_object_keys( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name=model_name) - for model_name in expected_model_names - ) - - -def _reference_dep( - *, reference: CompileSqlReference, seed_names: frozenset[str] = frozenset() -) -> CompiledObjectKey: - if reference.ref_kind == SqlReferenceKind.SOURCE: - return CompiledObjectKey( - resource_type=CompiledResourceType.SOURCE, - name=reference.ref_name, - ) - if reference.ref_kind == SqlReferenceKind.SEED: - return CompiledObjectKey( - resource_type=CompiledResourceType.SEED, - name=reference.ref_name, - ) - if reference.ref_kind == SqlReferenceKind.DBT_REF: - return CompiledObjectKey( - resource_type=CompiledResourceType.DBT_REF, - name=( - f"{reference.ref_package}.{reference.ref_name}" - if reference.ref_package is not None - else reference.ref_name - ), - ) - if reference.ref_kind == SqlReferenceKind.UDF: - return CompiledObjectKey( - resource_type=CompiledResourceType.UDF, - name=reference.ref_name, - ) - if reference.ref_kind == SqlReferenceKind.TABLE_FUNCTION: - return CompiledObjectKey( - resource_type=CompiledResourceType.TABLE_FN, - name=reference.ref_name, - ) - return CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, - name=reference.ref_name, - ) - - -def _dedupe_object_keys(keys: Iterable[CompiledObjectKey]) -> tuple[CompiledObjectKey, ...]: - deduped_keys: list[CompiledObjectKey] = [] - seen_keys: set[CompiledObjectKey] = set() - key: CompiledObjectKey - for key in keys: - if key in seen_keys: - continue - seen_keys.add(key) - deduped_keys.append(key) - return tuple(deduped_keys) diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py index 5d5f64545f..e328348fd4 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py @@ -8,11 +8,8 @@ from sqlbuild.compiler.compile._helpers.analysis.validation import ( hook_sql_statements, - validate_hook_sql_syntax, - validate_sql_syntax, ) from sqlbuild.compiler.compile.constants import UNNEEDED_SQL_ANALYSIS_OPT_OUT_CODE -from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( CompilerDiagnostic, SqlAnalysisOptOutRequest, @@ -77,31 +74,10 @@ def _parses(request: SqlAnalysisOptOutRequest) -> bool: statements: list[str] = [request.query_sql] for hook_name in _HOOK_KEYS: statements.extend(hook_sql_statements(request.config.values.get(hook_name))) - native: bool | None = check_native_sql_syntax( + return check_native_sql_syntax( checks=tuple((statement, request.placeholders) for statement in statements), dialect=None, ) - if native is not None: - return native - model_name: str = request.model_file.file_path.stem - try: - validate_sql_syntax( - query_sql=request.query_sql, - model_name=model_name, - file_path=request.model_file.file_path, - placeholders=request.placeholders, - ) - for hook_name in _HOOK_KEYS: - validate_hook_sql_syntax( - value=request.config.values.get(hook_name), - hook_name=hook_name, - model_name=model_name, - file_path=request.model_file.file_path, - placeholders=request.placeholders, - ) - except CompileInputError: - return False - return True def unneeded_opt_out_diagnostic( diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index da8311dd71..cef32f45df 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -26,8 +26,6 @@ DynamicColumnContractProof, ModelSqlAnalysis, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.project_assembly.main._assemble_native_project_resources import ( assemble_native_project_resources, @@ -51,10 +49,10 @@ def project_facts_by_engine( authoritative_column_types_by_table: dict[str, dict[str, str]], column_nullability_by_table: dict[str, dict[str, InferredNullability]], dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> NativeProjectFacts | None: - """Return the native project facts, or None where Python must derive all of them.""" +) -> NativeProjectFacts: + """Return the native project facts.""" - resources: NativeProjectResources | None = assemble_native_project_resources( + resources: NativeProjectResources = assemble_native_project_resources( inputs=inputs, dialect=dialect, syntax_checks=model_syntax_checks( @@ -67,8 +65,6 @@ def project_facts_by_engine( ), ), ) - if resources is None: - report_native_fallback(site=NativeFallbackSite.PROJECT_ASSEMBLY) pivots: dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]] = standalone_pivot_models( model_inputs=inputs.model_inputs, analyses=analyses ) @@ -92,9 +88,9 @@ def project_facts_by_engine( return NativeProjectFacts( models=tuple( NativeModelFacts( - deps=resources.model_deps[index] if resources is not None else None, + deps=resources.model_deps[index], dynamic_contract=proofs.get(index), - syntax_valid=resources is not None and resources.model_syntax_valid[index], + syntax_valid=resources.model_syntax_valid[index], ) for index in range(len(inputs.model_inputs)) ), diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index 1e630cb98c..892b1502c6 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -35,5 +35,4 @@ class NativeFallbackSite(StrEnum): SCOPE_REBIND_LOOKUP = "declaration_scopes.rebind_lookup" LINT_EXPANSION = "model_loop.lint_expansion" MACRO_CALL_MOCKED = "macro_calls.mocked_evaluation" - PROJECT_ASSEMBLY = "project_assembly.assembly" SQL_TEST_MACRO_MOCK_QUERIES = "sql_test_glue.macro_mock_queries" diff --git a/src/sqlbuild/compiler/project_assembly/_helpers/deferrals.py b/src/sqlbuild/compiler/project_assembly/_helpers/deferrals.py deleted file mode 100644 index 296c742539..0000000000 --- a/src/sqlbuild/compiler/project_assembly/_helpers/deferrals.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Debug-only record of the project assembly work native hands back to Python.""" - -from __future__ import annotations - -import json -import os -from pathlib import Path - -from sqlbuild.compiler.lineage.constants import ANALYSIS_DEFERRAL_RECORD_PREFIX -from sqlbuild.compiler.project_assembly.constants import PROJECT_ASSEMBLY_DEFERRAL_SITE -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR - - -def record_project_assembly_deferral(*, kind: str) -> None: - """Append one `{"kind", "site"}` line when the analysis record directory is set.""" - - directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) - if not directory: - return - target: Path = Path(directory) - target.mkdir(parents=True, exist_ok=True) - record: str = json.dumps({"kind": kind, "site": PROJECT_ASSEMBLY_DEFERRAL_SITE}) - with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: - _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/project_assembly/_helpers/request.py b/src/sqlbuild/compiler/project_assembly/_helpers/request.py index 9ad72253ca..1fdf58dc0f 100644 --- a/src/sqlbuild/compiler/project_assembly/_helpers/request.py +++ b/src/sqlbuild/compiler/project_assembly/_helpers/request.py @@ -6,6 +6,7 @@ import re from collections.abc import Iterable +from sqlbuild.compiler.authored_values.main._project_var_values import render_project_var_text from sqlbuild.compiler.compile.models import CompileProjectInputs, CompileSqlReference from sqlbuild.compiler.project_assembly.constants import ( BOOLEAN_VARIABLE, @@ -14,9 +15,13 @@ NATIVE_INT_MAX, NATIVE_INT_MIN, NONE_VARIABLE, + OPAQUE_VARIABLE, + RENDER_LABEL, + RENDER_RAISES, + RENDER_REJECTED, + RENDERED_TEXT, TEXT_VARIABLE, TRUE_TEXT, - UNSUPPORTED_VARIABLE, ) from sqlbuild.compiler.project_assembly.types import ( AuditRow, @@ -34,9 +39,12 @@ def project_request( inputs: CompileProjectInputs, dialect: str | None, syntax_checks: tuple[tuple[tuple[str, dict[str, str] | None], ...], ...], -) -> ProjectRequestRow: - """Return the request row; `syntax_checks` holds each model's SQL Python would validate.""" +) -> tuple[ProjectRequestRow, list[Exception | None]]: + """Return the request row and, per variable, the exception rendering it as text raises.""" + renders: list[tuple[str, str] | Exception | None] = [ + _opaque_render(value=value) for value in inputs.effective_vars.values() + ] target: TargetConfig | None = inputs.effective_target defaults: DefaultsConfig = inputs.project_config.defaults target_values: tuple[str | None, ...] = ( @@ -51,11 +59,16 @@ def project_request( seed_values: list[str | None] = [] for seed_input in inputs.seed_inputs: seed_values.extend((seed_input.schema_entry.database, seed_input.schema_entry.schema)) - return ( + row: ProjectRequestRow = ( dialect or "generic", (target.database, target.schema, target.loader_schema) if target is not None else None, (defaults.database, defaults.schema, defaults.seed_database, defaults.seed_schema), - [_variable_row(name=name, value=value) for name, value in inputs.effective_vars.items()], + [ + _variable_row(name=name, value=value, render=render, index=index) + for index, ((name, value), render) in enumerate( + zip(inputs.effective_vars.items(), renders, strict=True) + ) + ], _environment_rows((*target_values, *default_values, *seed_values)), [ ( @@ -103,6 +116,7 @@ def project_request( for audit_input in inputs.audit_inputs ], ) + return row, [render if isinstance(render, Exception) else None for render in renders] def _tag_rows(value: object) -> list[str]: @@ -123,16 +137,38 @@ def _environment_rows(values: Iterable[str | None]) -> list[tuple[str, str | Non return [(name, os.environ.get(name)) for name in names] -def _variable_row(*, name: str, value: object) -> VariableRow: +def _variable_row( + *, name: str, value: object, render: tuple[str, str] | Exception | None, index: int +) -> VariableRow: if value is None: - return (name, NONE_VARIABLE, "") + return (name, NONE_VARIABLE, "", None) if type(value) is bool: - return (name, BOOLEAN_VARIABLE, TRUE_TEXT if value else FALSE_TEXT) + return (name, BOOLEAN_VARIABLE, TRUE_TEXT if value else FALSE_TEXT, None) if type(value) is str: - return (name, TEXT_VARIABLE, value) + return (name, TEXT_VARIABLE, value, None) + if type(value) is int and NATIVE_INT_MIN <= value <= NATIVE_INT_MAX: + return (name, TEXT_VARIABLE, str(value), None) + held: tuple[str, str] = (RENDER_RAISES, str(index)) + return (name, OPAQUE_VARIABLE, str(value), held if isinstance(render, Exception) else render) + + +def _opaque_render(*, value: object) -> tuple[str, str] | Exception | None: + """`render_project_var_text` for any label: text, the message after the label, or the error.""" + + if value is None or type(value) in (bool, str): + return None if type(value) is int and NATIVE_INT_MIN <= value <= NATIVE_INT_MAX: - return (name, TEXT_VARIABLE, str(value)) - return (name, UNSUPPORTED_VARIABLE, "") + return None + try: + text: str = render_project_var_text(value=value, label=RENDER_LABEL) + except ValueError as error: + message: str = str(error) + if message.startswith(RENDER_LABEL): + return (RENDER_REJECTED, message[len(RENDER_LABEL) :]) + return error + except Exception as error: + return error + return (RENDERED_TEXT, text) def _reference_rows(references: tuple[CompileSqlReference, ...]) -> list[ReferenceRow]: diff --git a/src/sqlbuild/compiler/project_assembly/_helpers/resources.py b/src/sqlbuild/compiler/project_assembly/_helpers/resources.py index d5de61d0d6..0447acb07b 100644 --- a/src/sqlbuild/compiler/project_assembly/_helpers/resources.py +++ b/src/sqlbuild/compiler/project_assembly/_helpers/resources.py @@ -3,8 +3,11 @@ from __future__ import annotations from dataclasses import replace +from typing import cast +import sqlbuild._native as _native from sqlbuild.compiler.compile.constants import COMPILE_INPUT_READS +from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( CompiledObjectKey, CompiledRelationLocation, @@ -13,39 +16,76 @@ CompileSourceInput, ) from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.project_assembly.constants import ENVIRONMENT_READ -from sqlbuild.compiler.project_assembly.models import NativeProjectResources +from sqlbuild.compiler.project_assembly.constants import ( + COMPILE_INPUT_FAILURE, + ENVIRONMENT_READ, + HELD_FAILURE, + VALUE_FAILURE, +) +from sqlbuild.compiler.project_assembly.models import NativeProjectResources, NativeResourceFailure from sqlbuild.compiler.project_assembly.types import ( + FailureRow, NamespaceRow, ObjectKeyRow, ProjectResourcesRow, ) +from sqlbuild.compiler.sql_analysis.constants import NATIVE_FAILURE_PREFIX from sqlbuild.spec.contracts.models import SourceEntry def project_resources( - *, inputs: CompileProjectInputs, row: ProjectResourcesRow + *, inputs: CompileProjectInputs, row: ProjectResourcesRow, held: list[Exception | None] ) -> NativeProjectResources: - """Return the resource facts the native row describes, in input order.""" + """Return the resource facts the native row describes, in input order, up to the failure.""" - model_deps, source_namespaces, seed_namespaces, function_deps, audit_deps, reads, syntax = row + ( + model_deps, + source_namespaces, + seed_namespaces, + function_deps, + audit_deps, + reads, + syntax, + failure, + ) = row _ = [_replay_read(kind=kind, name=name) for kind, name in reads] return NativeProjectResources( model_deps=tuple(_keys(keys) for keys in model_deps), model_syntax_valid=tuple(syntax), source_entries=tuple( _source_entry(source_input=source_input, namespace=namespace) - for source_input, namespace in zip(inputs.source_inputs, source_namespaces, strict=True) + for source_input, namespace in zip( + inputs.source_inputs[: len(source_namespaces)], source_namespaces, strict=True + ) ), seed_destinations=tuple( _seed_destination(seed_input=seed_input, namespace=namespace) - for seed_input, namespace in zip(inputs.seed_inputs, seed_namespaces, strict=True) + for seed_input, namespace in zip( + inputs.seed_inputs[: len(seed_namespaces)], seed_namespaces, strict=True + ) ), function_deps=tuple(_keys(keys) for keys in function_deps), audit_scope_deps=tuple(_keys(keys) for keys in audit_deps), + failure=None if failure is None else _failure(row=failure, held=held), ) +def _failure(*, row: FailureRow, held: list[Exception | None]) -> NativeResourceFailure: + slot, index, kind, message, help_text, held_index = row + error: Exception + if kind == COMPILE_INPUT_FAILURE: + error = CompileInputError(message, help=help_text) + elif kind == VALUE_FAILURE: + error = ValueError(message) + elif kind == HELD_FAILURE and held_index is not None and held[held_index] is not None: + error = cast(Exception, held[held_index]) + else: + raise _native.NativeCompilerError( + f"{NATIVE_FAILURE_PREFIX}native project assembly: unknown failure kind '{kind}'" + ) + return NativeResourceFailure(slot=slot, index=index, error=error) + + def _replay_read(*, kind: str, name: str) -> None: if kind == ENVIRONMENT_READ: COMPILE_INPUT_READS.environment_read(name) diff --git a/src/sqlbuild/compiler/project_assembly/constants.py b/src/sqlbuild/compiler/project_assembly/constants.py index 9b809a1ecd..6435d36097 100644 --- a/src/sqlbuild/compiler/project_assembly/constants.py +++ b/src/sqlbuild/compiler/project_assembly/constants.py @@ -1,10 +1,19 @@ -"""Native project assembly request kinds and deferral record site.""" +"""Native project assembly request and failure kinds.""" -PROJECT_ASSEMBLY_DEFERRAL_SITE: str = "project_assembly" TEXT_VARIABLE: str = "text" BOOLEAN_VARIABLE: str = "bool" NONE_VARIABLE: str = "none" -UNSUPPORTED_VARIABLE: str = "unsupported" +OPAQUE_VARIABLE: str = "opaque" +RENDERED_TEXT: str = "text" +RENDER_REJECTED: str = "rejected" +RENDER_RAISES: str = "raises" +RENDER_LABEL: str = "\x00" +COMPILE_INPUT_FAILURE: str = "compile_input" +VALUE_FAILURE: str = "value" +HELD_FAILURE: str = "held" +SOURCE_SLOT: str = "source" +SEED_SLOT: str = "seed" +AUDIT_SLOT: str = "audit" TRUE_TEXT: str = "1" FALSE_TEXT: str = "0" NATIVE_INT_MIN: int = -(2**63) diff --git a/src/sqlbuild/compiler/project_assembly/main/_assemble_native_project_resources.py b/src/sqlbuild/compiler/project_assembly/main/_assemble_native_project_resources.py index fd3b5d63d0..215c614cd4 100644 --- a/src/sqlbuild/compiler/project_assembly/main/_assemble_native_project_resources.py +++ b/src/sqlbuild/compiler/project_assembly/main/_assemble_native_project_resources.py @@ -1,14 +1,13 @@ -"""Assemble the compiled project's resource facts natively for the preview compiler engine.""" +"""Assemble the compiled project's resource facts natively.""" from __future__ import annotations import sqlbuild._native as _native from sqlbuild.compiler.compile.models import CompileProjectInputs -from sqlbuild.compiler.project_assembly._helpers.deferrals import record_project_assembly_deferral from sqlbuild.compiler.project_assembly._helpers.request import project_request from sqlbuild.compiler.project_assembly._helpers.resources import project_resources from sqlbuild.compiler.project_assembly.models import NativeProjectResources -from sqlbuild.compiler.project_assembly.types import ProjectResourcesRow +from sqlbuild.compiler.project_assembly.types import ProjectRequestRow, ProjectResourcesRow def assemble_native_project_resources( @@ -16,15 +15,11 @@ def assemble_native_project_resources( inputs: CompileProjectInputs, dialect: str | None, syntax_checks: tuple[tuple[tuple[str, dict[str, str] | None], ...], ...], -) -> NativeProjectResources | None: - """Return the resource facts, or None with a deferral record where Python must assemble.""" +) -> NativeProjectResources: + """Return the resource facts and the resource Python's assembly raises at, if any.""" - row: ProjectResourcesRow | None - deferral: str | None - row, deferral = _native.assemble_project_resource_facts( - project_request(inputs=inputs, dialect=dialect, syntax_checks=syntax_checks) - ) - if row is None: - _ = record_project_assembly_deferral(kind=deferral or "unknown") - return None - return project_resources(inputs=inputs, row=row) + request: ProjectRequestRow + held: list[Exception | None] + request, held = project_request(inputs=inputs, dialect=dialect, syntax_checks=syntax_checks) + row: ProjectResourcesRow = _native.assemble_project_resource_facts(request) + return project_resources(inputs=inputs, row=row, held=held) diff --git a/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py b/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py index a929ed239f..0a00801549 100644 --- a/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py +++ b/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py @@ -1,24 +1,18 @@ -"""Check SQL syntax natively for the preview compiler engine.""" +"""Check SQL syntax natively.""" from __future__ import annotations import sqlbuild._native as _native -from sqlbuild.compiler.project_assembly._helpers.deferrals import record_project_assembly_deferral def check_native_sql_syntax( *, checks: tuple[tuple[str, dict[str, str] | None], ...], dialect: str | None -) -> bool | None: - """Whether Python's syntax validation accepts every `(sql, placeholders)`; None to check it.""" +) -> bool: + """Whether every `(sql, placeholders)` parses; raises Python's pre-parse `ValueError`.""" - valid: bool | None - deferral: str | None - valid, deferral = _native.check_native_sql_syntax( + return _native.check_native_sql_syntax( ( dialect or "generic", [(sql, list((placeholders or {}).items())) for sql, placeholders in checks], ) ) - if valid is None: - _ = record_project_assembly_deferral(kind=deferral or "unknown") - return valid diff --git a/src/sqlbuild/compiler/project_assembly/main/_native_resource.py b/src/sqlbuild/compiler/project_assembly/main/_native_resource.py new file mode 100644 index 0000000000..61e833139a --- /dev/null +++ b/src/sqlbuild/compiler/project_assembly/main/_native_resource.py @@ -0,0 +1,16 @@ +"""Take one resource's native fact, raising the error Python's assembly raises there.""" + +from __future__ import annotations + +from sqlbuild.compiler.project_assembly.models import NativeProjectResources, NativeResourceFailure + + +def native_resource[T]( + *, resources: NativeProjectResources, slot: str, items: tuple[T, ...], index: int +) -> T: + """Return resource `index` of `slot`, raising the error Python's assembly raises there.""" + + failure: NativeResourceFailure | None = resources.failure + if failure is not None and failure.slot == slot and failure.index == index: + raise failure.error + return items[index] diff --git a/src/sqlbuild/compiler/project_assembly/models.py b/src/sqlbuild/compiler/project_assembly/models.py index 4d12b8182e..89cb67c322 100644 --- a/src/sqlbuild/compiler/project_assembly/models.py +++ b/src/sqlbuild/compiler/project_assembly/models.py @@ -22,20 +22,30 @@ class NativeProjectResources: seed_destinations: tuple[CompiledRelationLocation, ...] function_deps: tuple[tuple[CompiledObjectKey, ...], ...] audit_scope_deps: tuple[tuple[CompiledObjectKey, ...], ...] + failure: NativeResourceFailure | None = None + + +@dataclass(frozen=True, slots=True) +class NativeResourceFailure: + """The resource Python's assembly raises at, and the exception it raises there.""" + + slot: str + index: int + error: Exception @dataclass(frozen=True, slots=True) class NativeModelFacts: - """One model's native facts; each None where Python derives it.""" + """One model's native facts.""" - deps: tuple[CompiledObjectKey, ...] | None + deps: tuple[CompiledObjectKey, ...] dynamic_contract: DynamicColumnContractProof | None syntax_valid: bool = False @dataclass(frozen=True, slots=True) class NativeProjectFacts: - """Every model's native facts, and the resource facts unless Python must assemble them.""" + """Every model's native facts and the resource facts.""" models: tuple[NativeModelFacts, ...] - resources: NativeProjectResources | None + resources: NativeProjectResources diff --git a/src/sqlbuild/compiler/project_assembly/types.py b/src/sqlbuild/compiler/project_assembly/types.py index 057110b741..afcec02958 100644 --- a/src/sqlbuild/compiler/project_assembly/types.py +++ b/src/sqlbuild/compiler/project_assembly/types.py @@ -4,7 +4,8 @@ type ReferenceRow = tuple[str, str, str | None] type SyntaxCheckRow = tuple[str, list[tuple[str, str]]] -type VariableRow = tuple[str, str, str] +type VariableRow = tuple[str, str, str, tuple[str, str] | None] +type FailureRow = tuple[str, int, str, str, str | None, int | None] type ModelRow = tuple[str, str, list[str], list[ReferenceRow], list[SyntaxCheckRow]] type SourceRow = tuple[str, bool, str | None, str | None] type SeedRow = tuple[str, list[str], str | None, str | None] @@ -34,4 +35,5 @@ list[list[ObjectKeyRow]], list[tuple[str, str]], list[bool], + FailureRow | None, ] diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 34b7fc3fca..0be911fcd0 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -99,12 +99,6 @@ def _perturbed(self, **changes): _record.write(json.dumps({"kind": "legacy_fallback", "site": "orders.sql"}) + "\\n") """ -PROJECT_ASSEMBLY_SABOTAGE: str = """ -import sqlbuild._native as native - -native.assemble_project_resource_facts = lambda *_args: (None, "sabotaged") -""" - MODEL_ANALYSIS_SABOTAGE: str = """ import sqlbuild._native as native diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 6a58b0dab2..464f38c5ee 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -10,7 +10,6 @@ from tests.e2e.scripts.compiler_differential._test_types import NativeFallbackGateTestCase from tests.e2e.scripts.compiler_differential.helpers import ( MODEL_ANALYSIS_SABOTAGE, - PROJECT_ASSEMBLY_SABOTAGE, harness_arguments, write_native_perturbation, ) @@ -42,17 +41,6 @@ "from the allow-list", ), ), - NativeFallbackGateTestCase( - description="shipped_project_assembly_sabotaged_to_python_fails", - perturbation=PROJECT_ASSEMBLY_SABOTAGE, - appended_entries="", - expected_exit_code=1, - expected_lines=( - "Native fallback allow-list: native-preview project_assembly " - "project_assembly.assembly deferred (project): ", - "not on the allow-list; port it or list it with a reason", - ), - ), NativeFallbackGateTestCase( description="preview_model_analysis_sabotaged_to_python_fails", perturbation=MODEL_ANALYSIS_SABOTAGE, diff --git a/tests/goldens/python_oracle/project_assembly_error_and_python_value_edges.json b/tests/goldens/python_oracle/project_assembly_error_and_python_value_edges.json new file mode 100644 index 0000000000..c8a3d30096 --- /dev/null +++ b/tests/goldens/python_oracle/project_assembly_error_and_python_value_edges.json @@ -0,0 +1,122 @@ +[ +[ +"project_assembly", +"b0da4db3b091cc425f7fbe09" +], +[ +"project_assembly", +"a1d218340a8a83ce8681f2b2" +], +[ +"project_assembly", +"8a47cd3642ace755a7c46b94" +], +[ +"project_assembly", +"22b5e7c6bf3587610e7b1b7e" +], +[ +"project_assembly", +"ac339c15fbbcb948eb69da7e" +], +[ +"project_assembly", +"56459e971d6fe0e49ff51ed5" +], +[ +"project_assembly", +"568de5c3f4f1b83304fab4cb" +], +[ +"project_assembly", +"c7887fba33496eea6d479a73" +], +[ +"project_assembly", +"bddc5553ccdd80aea43a1dc2" +], +[ +"project_assembly", +"cad31f1d08d77478fa7b2ae6" +], +[ +"project_assembly", +"bddc5553ccdd80aea43a1dc2" +], +[ +"project_assembly", +"e2a1f01e91327cd87c4ce2df" +], +[ +"project_assembly", +"800a1eb0f3c23e14edfd39d9" +], +[ +"project_assembly", +"b1951da418d038d9394fa019" +], +[ +"project_assembly", +"9d5591dd12ddd428dc6c8e56" +], +[ +"project_assembly", +"bddc5553ccdd80aea43a1dc2" +], +[ +"project_assembly", +"f0a5ab0aedffce53d0bac0bf" +], +[ +"project_assembly", +"44cdecb987fae0e4644b1a08" +], +[ +"project_assembly", +"b5a2c667add3dec4527f8ef7" +], +[ +"project_assembly", +"1a55871ff35d8dc0260773bd" +], +[ +"project_assembly", +"2df742bfc91738b084725927" +], +[ +"project_assembly", +"bcc774439d9bbe2be64ac4b2" +], +[ +"project_assembly", +"040cd531ebc642bf4de669da" +], +[ +"project_assembly", +"7ee2dc900563b2b129038429" +], +[ +"project_assembly", +"8bca33d40133968f5dab80e4" +], +[ +"project_assembly", +"b6f56c4fa9f2885afc8f713e" +], +[ +"project_assembly", +"f0a5ab0aedffce53d0bac0bf" +], +[ +"project_assembly", +"9ab8c9ca08cfda43b74ff42f" +], +[ +"project_assembly", +"891503d02bf0c99bc3bc98c7" +], +[ +"project_assembly", +"cdd9d32ea79d9111e6be4551" +] +] diff --git a/tests/goldens/python_oracle/project_assembly_managed_sources_templated_seeds_and_targets_hooks_audits_a_function.json b/tests/goldens/python_oracle/project_assembly_managed_sources_templated_seeds_and_targets_hooks_audits_a_function.json new file mode 100644 index 0000000000..e03d4f59aa --- /dev/null +++ b/tests/goldens/python_oracle/project_assembly_managed_sources_templated_seeds_and_targets_hooks_audits_a_function.json @@ -0,0 +1,34 @@ +[ +[ +"project_assembly", +"c624532f45ee2f49f32bb014" +], +[ +"project_assembly", +"e8f758602c1eff42b55ae130" +], +[ +"project_assembly", +"c864759e0faad475584471c2" +], +[ +"project_assembly", +"bef8fb45b929dfd76002a363" +], +[ +"project_assembly", +"72eebcec28c52bab2cfaff77" +], +[ +"project_assembly", +"545af810ba171c3734719656" +], +[ +"project_assembly", +"69e7548205912c60d17e51ba" +], +[ +"project_assembly", +"2d3e75fa9a354fbf256f6bcb" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py index 96437c806e..4377317896 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py @@ -12,29 +12,25 @@ class GeneratedAssemblyParityTestCase: count: int model_count: int environment: dict[str, str] - expected_minimum_python_calls: int expected_minimum_environment_reads: int @dataclass(frozen=True) class AssemblyDeferralTestCase: - """A project Python rejects: native raises the same error, deferring only where recorded.""" + """A project whose model SQL Python rejects with a syntax error.""" description: str files: dict[str, str] - expected_kind: str | None expected_error: str @dataclass(frozen=True) -class DeferredAssemblyTestCase: - """A project native hands back to Python, which assembles it without error.""" +class EdgeAssemblyTestCase: + """Projects whose assembly raises or reads Python-only values, against Python's golden.""" description: str - effective_vars: dict[str, object] - seed_schema: str - expected_schema: str - expected_kind: str + projects: tuple[tuple[str, dict[str, str], dict[str, object] | None], ...] + expected_raised: int @dataclass(frozen=True) @@ -54,7 +50,6 @@ class OptOutTestCase: description: str models: dict[str, str] expected_rejected: dict[str, bool] - expected_python_validations: int @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py index 3a498c9d7a..b61931c5ed 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py @@ -2,13 +2,13 @@ from __future__ import annotations +import datetime import os import random from collections import Counter from collections.abc import Callable -from operator import methodcaller +from dataclasses import replace from pathlib import Path -from types import ModuleType from typing import Any import pytest @@ -17,7 +17,6 @@ from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile._helpers.assembly import project as project_module from sqlbuild.compiler.compile._helpers.assembly.project import assemble_compiled_project -from sqlbuild.compiler.compile._helpers.diagnostics import sql_analysis_opt_outs from sqlbuild.compiler.compile.constants import COMPILE_INPUT_READS from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( @@ -30,22 +29,8 @@ from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.compiler.project_assembly.types import ProjectRequestRow from sqlbuild.sql_values.types import CollectionRendering -from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import deferral_kinds - -_PYTHON_RESOURCE_FUNCTIONS: tuple[str, ...] = ( - "_build_source_relation_entry", - "build_seed_relation_target", - "validate_sql_syntax", - "validate_hook_sql_syntax", - "model_build_deps", - "function_build_deps", - "audit_scope_deps", -) -_ASSEMBLY_RECORD_PREFIX: str = "project_assembly:" -_OPT_OUT_VALIDATORS: tuple[str, ...] = ("validate_sql_syntax", "validate_hook_sql_syntax") -type NativeDeferrals = tuple[tuple[ModuleType, str], ...] -DEFERRED_PROJECT_FACTS: NativeDeferrals = ((project_module, "project_facts_by_engine"),) -DEFERRED_SYNTAX_CHECK: NativeDeferrals = ((sql_analysis_opt_outs, "check_native_sql_syntax"),) + +_PYTHON_RESOURCE_FUNCTIONS: tuple[str, ...] = ("validate_sql_syntax", "validate_hook_sql_syntax") _ADAPTER_CONTEXT: CompileAdapterContext = CompileAdapterContext( value_renderer=DuckDbAdapter(), collection_rendering=CollectionRendering.VALUE_LIST, @@ -174,36 +159,55 @@ def project_inputs(*, project_dir: Path, files: dict[str, str]) -> CompileProjec def assemble_with( - *, - inputs: CompileProjectInputs, - engine: CompilerEngine, - monkeypatch: pytest.MonkeyPatch, - deferred: NativeDeferrals = (), + *, inputs: CompileProjectInputs, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch ) -> CompiledProject: - """Assemble `inputs` with `engine`, making each native entry in `deferred` defer to Python.""" + """Assemble `inputs` with `engine`.""" monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - with monkeypatch.context() as patch: - _ = [patch.setattr(module, name, _deferral) for module, name in deferred] - return assemble_compiled_project(inputs=inputs) + return assemble_compiled_project(inputs=inputs) def recorded_assembly( - *, - inputs: CompileProjectInputs, - engine: CompilerEngine, - monkeypatch: pytest.MonkeyPatch, - deferred: NativeDeferrals = (), + *, inputs: CompileProjectInputs, engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch ) -> tuple[object, tuple[tuple[str, ...], bool]]: """Assemble `inputs` with `engine`; return its assembly view and the input reads recorded.""" with COMPILE_INPUT_READS.recording() as reads: project: CompiledProject = assemble_with( - inputs=inputs, engine=engine, monkeypatch=monkeypatch, deferred=deferred + inputs=inputs, engine=engine, monkeypatch=monkeypatch ) return assembly_view(project), (reads.environment_names, reads.read_run_id) +def assembly_outcome( + *, + project_dir: Path, + files: dict[str, str], + effective_vars: dict[str, object] | None, + monkeypatch: pytest.MonkeyPatch, +) -> tuple[object, ...]: + """Attach and assemble `files`: `("ok", view)`, or the error's class, message, help and code.""" + + try: + inputs: CompileProjectInputs = project_inputs(project_dir=project_dir, files=files) + merged: CompileProjectInputs = replace( + inputs, effective_vars={**inputs.effective_vars, **(effective_vars or {})} + ) + return ( + "ok", + assembly_view( + assemble_with(inputs=merged, engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch) + ), + ) + except Exception as error: + return ( + type(error).__name__, + str(error).replace(str(project_dir), ""), + getattr(error, "help", None), + getattr(error, "code", None), + ) + + def assembly_view(project: CompiledProject) -> object: """Return every resource fact native project assembly derives, with the diagnostics.""" @@ -223,49 +227,19 @@ def rejected_opt_outs( files: dict[str, str], engine: CompilerEngine, monkeypatch: pytest.MonkeyPatch, - deferred: NativeDeferrals = (), -) -> tuple[dict[str, bool], int]: - """Attach `files` with `engine`; return each opt-out's rejection and Python validations. - - Each native entry in `deferred` defers, so Python answers its work.""" +) -> dict[str, bool]: + """Attach `files` with `engine`; return whether each opt-out is rejected.""" - calls: Counter[str] = Counter() monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) - with monkeypatch.context() as patch: - _ = [patch.setattr(module, name, _deferral) for module, name in deferred] - _ = [ - patch.setattr( - sql_analysis_opt_outs, - name, - _counted(name=name, function=getattr(sql_analysis_opt_outs, name), calls=calls), - ) - for name in _OPT_OUT_VALIDATORS - ] - inputs: CompileProjectInputs = project_inputs(project_dir=project_dir, files=files) - return ( - { - model_input.model_file.file_path.stem: ( - model_input.rejected_sql_analysis_opt_out is not None - ) - for model_input in inputs.model_inputs - }, - calls.total(), - ) - - -def assembly_deferrals(record_dir: Path) -> Counter[str]: - """Count the project assembly `site:kind` deferral records below `record_dir`.""" - - return Counter( - filter( - methodcaller("startswith", _ASSEMBLY_RECORD_PREFIX), - deferral_kinds(record_dir).elements(), - ) - ) + inputs: CompileProjectInputs = project_inputs(project_dir=project_dir, files=files) + return { + model_input.model_file.file_path.stem: model_input.rejected_sql_analysis_opt_out is not None + for model_input in inputs.model_inputs + } def python_resource_calls(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count calls to Python's resource-fact functions from project assembly.""" + """Count calls to Python's model syntax error reporting from project assembly.""" calls: Counter[str] = Counter() _ = [ @@ -306,5 +280,303 @@ def counted(*args: Any, **kwargs: Any) -> Any: return counted -def _deferral(**_kwargs: object) -> None: - return None +_EDGE_PROJECT: str = ( + 'name = "assembly"\nadapter = "duckdb"\ndefault_target = "dev"\n\n' + '[connection]\ndatabase = ":memory:"\n\n' +) +_EDGE_MODEL: str = ( + 'MODEL (description "Orders", database "lake", schema "orders");\n\nSELECT 1 AS n\n' +) +_EDGE_SOURCE: str = ( + "sources:\n - name: raw_orders\n description: Raw orders.\n managed: true\n" + " write_strategy: table\n{ns} columns:\n - name: n\n type: INTEGER\n" +) +_EDGE_LOADER: str = ( + "from sqlbuild.loaders import loader\n\n\n" + "@loader\ndef raw_orders(ctx):\n '''Load raw orders.'''\n return [{'n': 1}]\n" +) +_EDGE_VARS: dict[str, object] = { + "tags": ["daily", "x"], + "meta": {"b": 1, "a": "z"}, + "ratio": 1.5, + "day": datetime.date(2024, 1, 2), + "big": 10**30, + "flag": True, + "none": None, + "mixed": [datetime.date(2024, 1, 2)], + "name": "prod", +} + + +def _edge_seeds(*namespaces: str) -> str: + return "seeds:\n" + "".join( + f" - name: s{index}\n description: Seed {index}.\n{namespace}" + " columns:\n - name: n\n type: INTEGER\n" + for index, namespace in enumerate(namespaces) + ) + + +def _edge_seed_files(*namespaces: str) -> dict[str, str]: + return { + "seeds/lookups.yml": _edge_seeds(*namespaces), + "models/orders.sql": _EDGE_MODEL, + **{f"seeds/s{index}.csv": "n\n1\n" for index in range(len(namespaces))}, + } + + +def _edge_target(text: str) -> str: + return _EDGE_PROJECT + "[targets.dev]\n" + text + + +def _edge_source_files(ns: str = "") -> dict[str, str]: + return { + "sources/raw.yml": _EDGE_SOURCE.format(ns=ns), + "python/loaders/raw.py": _EDGE_LOADER, + "models/orders.sql": _EDGE_MODEL, + } + + +# Projects whose assembly raises, or reads variables only Python renders: (description, files, +# effective vars merged over the project's). +EDGE_ASSEMBLY_PROJECTS: tuple[tuple[str, dict[str, str], dict[str, object] | None], ...] = ( + ( + "seed schema missing env", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${ENV:SQB_EDGE_UNSET}"\n'), + }, + None, + ), + ( + "seed database unknown var", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' database: "${var:nope}"\n'), + }, + None, + ), + ( + "seed unknown ctx", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${CTX:model.nope}"\n'), + }, + None, + ), + ( + "seed template syntax", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${coalesce(}"\n'), + }, + None, + ), + ( + "second seed fails after first", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "ok"\n', ' schema: "${ENV:SQB_EDGE_UNSET}"\n'), + }, + None, + ), + ( + "preserved schema seed", + {"sqlbuild_project.toml": _edge_target('schema = "preserve"\n'), **_edge_seed_files("")}, + None, + ), + ( + "preserved database and schema seed", + { + "sqlbuild_project.toml": _edge_target('database = "preserve"\nschema = "preserve"\n'), + **_edge_seed_files(""), + }, + None, + ), + ( + "preserved managed source", + { + "sqlbuild_project.toml": _edge_target('database = "preserve"\nschema = "dev"\n'), + **_edge_source_files(), + }, + None, + ), + ( + "managed source target missing env", + { + "sqlbuild_project.toml": _edge_target('schema = "${ENV:SQB_EDGE_UNSET}"\n'), + **_edge_source_files(), + }, + None, + ), + ( + "managed source loader schema unknown var", + { + "sqlbuild_project.toml": _edge_target( + 'schema = "dev"\nloader_schema = "${var:nope}"\n' + ), + **_edge_source_files(), + }, + None, + ), + ( + "target schema override missing env", + { + "sqlbuild_project.toml": _edge_target('schema = "${ENV:SQB_EDGE_UNSET}"\n'), + **_edge_seed_files(""), + }, + None, + ), + ( + "target database override unknown var", + { + "sqlbuild_project.toml": _edge_target('database = "${var:nope}"\nschema = "dev"\n'), + **_edge_seed_files(""), + }, + None, + ), + ( + "default seed schema unknown var", + { + "sqlbuild_project.toml": _EDGE_PROJECT + + '[defaults]\nseed_schema = "${var:nope}"\n\n[targets.dev]\nschema = "dev"\n', + **_edge_seed_files(""), + }, + None, + ), + ( + "default database missing env", + { + "sqlbuild_project.toml": _EDGE_PROJECT + + '[defaults]\ndatabase = "${ENV:SQB_EDGE_UNSET}"\n\n[targets.dev]\nschema = "dev"\n', + **_edge_seed_files(""), + }, + None, + ), + ( + "model syntax error before seed error", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${ENV:SQB_EDGE_UNSET}"\n'), + "models/orders.sql": 'MODEL (description "Orders");\n\nSELECT FROM WHERE (\n', + }, + None, + ), + ( + "source error before seed error", + { + "sqlbuild_project.toml": _edge_target('schema = "${ENV:SQB_EDGE_UNSET}"\n'), + **_edge_source_files(), + "seeds/lookups.yml": _edge_seeds(' schema: "${var:nope}"\n'), + "seeds/s0.csv": "n\n1\n", + }, + None, + ), + ( + "list var whole", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${tags}"\n'), + }, + _EDGE_VARS, + ), + ( + "list var in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "s_${tags}"\n'), + }, + _EDGE_VARS, + ), + ( + "dict var whole", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${meta}"\n'), + }, + _EDGE_VARS, + ), + ( + "dict var in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "s_${var:meta}"\n'), + }, + _EDGE_VARS, + ), + ( + "float var whole and in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${ratio}"\n', ' schema: "s_${ratio}"\n'), + }, + _EDGE_VARS, + ), + ( + "date var whole", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${day}"\n'), + }, + _EDGE_VARS, + ), + ( + "date var in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "s_${day}"\n'), + }, + _EDGE_VARS, + ), + ( + "big int var whole and in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${big}"\n', ' database: "d_${big}"\n'), + }, + _EDGE_VARS, + ), + ( + "bool and none vars", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "${flag}"\n', ' schema: "s_${none}_${flag}"\n'), + }, + _EDGE_VARS, + ), + ( + "list of dates in text", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(' schema: "s_${mixed}"\n'), + }, + _EDGE_VARS, + ), + ( + "list var in coalesce", + { + "sqlbuild_project.toml": _edge_target('schema = "dev"\n'), + **_edge_seed_files(" schema: \"${coalesce(tags, 'x')}\"\n"), + }, + _EDGE_VARS, + ), + ( + "dict var in target schema text", + {"sqlbuild_project.toml": _edge_target('schema = "t_${meta}"\n'), **_edge_seed_files("")}, + _EDGE_VARS, + ), + ( + "float var in managed source target", + {"sqlbuild_project.toml": _edge_target('schema = "${ratio}"\n'), **_edge_source_files()}, + _EDGE_VARS, + ), + ( + "unused opaque vars", + { + "sqlbuild_project.toml": _EDGE_PROJECT + + '[vars]\nname = "prod"\n\n[targets.dev]\nschema = "dev_${name}"\n', + **_edge_seed_files(' schema: "s_${name}"\n'), + **_edge_source_files(), + }, + _EDGE_VARS, + ), +) diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index 185b175337..f7cf6d8ef6 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -1,11 +1,10 @@ -"""Native project assembly equals Python's resource assembly, or hands the project back.""" +"""Native project assembly equals Python's recorded resource assembly and its errors.""" from __future__ import annotations import os import random from collections import Counter -from dataclasses import replace from pathlib import Path import pytest @@ -18,11 +17,16 @@ from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.compiler.project_assembly.types import ProjectRequestRow -from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR -from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) from tests.integration.src.sqlbuild.compiler.project_assembly._test_types import ( AssemblyDeferralTestCase, - DeferredAssemblyTestCase, + EdgeAssemblyTestCase, GeneratedAssemblyParityTestCase, GraphFactsTestCase, NativePanicTestCase, @@ -30,11 +34,9 @@ WindowsEnvironmentTestCase, ) from tests.integration.src.sqlbuild.compiler.project_assembly.helpers import ( - DEFERRED_PROJECT_FACTS, - DEFERRED_SYNTAX_CHECK, + EDGE_ASSEMBLY_PROJECTS, assemble_with, - assembly_deferrals, - assembly_view, + assembly_outcome, generated_assembly_files, project_inputs, python_resource_calls, @@ -69,58 +71,87 @@ count=8, model_count=12, environment={"SQB_ASSEMBLY_LOADERS": "loaders"}, - expected_minimum_python_calls=200, expected_minimum_environment_reads=4, ) ], ids=lambda case: case.description, ) -def test_given_generated_projects_when_assembling_natively_then_matches_python( +def test_given_generated_projects_when_assembling_natively_then_matches_python_golden( test_case: GeneratedAssemblyParityTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - record_dir: Path = tmp_path / "records" - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) _ = [monkeypatch.setenv(name, value) for name, value in test_case.environment.items()] calls: Counter[str] = python_resource_calls(monkeypatch=monkeypatch) rng: random.Random = random.Random(test_case.seed) + project_dirs: list[Path] = [tmp_path / f"project_{index}" for index in range(test_case.count)] inputs: list[CompileProjectInputs] = [ project_inputs( - project_dir=tmp_path / f"project_{index}", + project_dir=project_dir, files=generated_assembly_files(rng=rng, model_count=test_case.model_count), ) - for index in range(test_case.count) - ] - python: list[tuple[object, tuple[tuple[str, ...], bool]]] = [ - recorded_assembly( - inputs=item, - engine=CompilerEngine.NATIVE, - monkeypatch=monkeypatch, - deferred=DEFERRED_PROJECT_FACTS, - ) - for item in inputs + for project_dir in project_dirs ] - python_calls: int = calls.total() native: list[tuple[object, tuple[tuple[str, ...], bool]]] = [ - recorded_assembly( - inputs=item, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch - ) + recorded_assembly(inputs=item, engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch) for item in inputs ] + golden: list[GoldenEntry] = [ + golden_entry("project_assembly", view, masked=(str(project_dir),)) + for project_dir, view in zip(project_dirs, native, strict=True) + ] assert ( - mismatches( - inputs=list(range(test_case.count)), - expected=list[object](python), - actual=list[object](native), + golden_differences( + read_golden(golden_name("project_assembly", test_case.description)), golden ) == [] ) assert sum(len(reads[0]) for _, reads in native) >= test_case.expected_minimum_environment_reads - assert python_calls >= test_case.expected_minimum_python_calls - assert calls.total() == python_calls - assert assembly_deferrals(record_dir) == Counter() + assert calls == Counter() + + +@pytest.mark.parametrize( + "test_case", + [ + EdgeAssemblyTestCase( + description="error and python value edges", + projects=EDGE_ASSEMBLY_PROJECTS, + expected_raised=22, + ) + ], + ids=lambda case: case.description, +) +def test_given_projects_python_rejects_when_assembling_natively_then_matches_python_golden( + test_case: EdgeAssemblyTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.delenv("SQB_EDGE_UNSET", raising=False) + project_dirs: list[Path] = [ + tmp_path / f"edge_{index}" for index in range(len(test_case.projects)) + ] + outcomes: list[tuple[object, ...]] = [ + assembly_outcome( + project_dir=project_dir, + files=files, + effective_vars=effective_vars, + monkeypatch=monkeypatch, + ) + for project_dir, (_, files, effective_vars) in zip( + project_dirs, test_case.projects, strict=True + ) + ] + golden: list[GoldenEntry] = [ + golden_entry("project_assembly", outcome, masked=(str(project_dir),)) + for project_dir, outcome in zip(project_dirs, outcomes, strict=True) + ] + + assert ( + golden_differences( + read_golden(golden_name("project_assembly", test_case.description)), golden + ) + == [] + ) + assert sum(outcome[0] != "ok" for outcome in outcomes) == test_case.expected_raised @pytest.mark.parametrize( @@ -132,7 +163,6 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( "sqlbuild_project.toml": _PROJECT_TOML, "models/orders.sql": 'MODEL (description "Orders");\n\nSELECT FROM WHERE (\n', }, - expected_kind=None, expected_error="SQL syntax error in model 'orders'", ), AssemblyDeferralTestCase( @@ -144,114 +174,22 @@ def test_given_generated_projects_when_assembling_natively_then_matches_python( ");\n\nSELECT 1 AS n\n" ), }, - expected_kind=None, expected_error="Polyglot could not parse model 'orders' pre_hooks[0]", ), - AssemblyDeferralTestCase( - description="a seed schema reading an unset environment variable", - files={ - "sqlbuild_project.toml": _PROJECT_TOML, - "seeds/lookups.yml": seed_yml("${ENV:SQB_UNSET_SEED_SCHEMA}"), - **_SEED_FILES, - }, - expected_kind="target_template", - expected_error="references missing ENV variable 'SQB_UNSET_SEED_SCHEMA'", - ), - AssemblyDeferralTestCase( - description="a preserved target schema the seed does not own", - files={ - "sqlbuild_project.toml": ( - 'name = "assembly"\nadapter = "duckdb"\ndefault_target = "dev"\n\n' - '[connection]\ndatabase = ":memory:"\n\n[targets.dev]\nschema = "preserve"\n' - ), - "seeds/lookups.yml": ( - "seeds:\n - name: countries\n description: Countries.\n" - " columns:\n - name: n\n type: INTEGER\n" - ), - "seeds/countries.csv": "n\n1\n", - "models/orders.sql": ( - 'MODEL (description "Orders", schema "orders");\n\nSELECT 1 AS n\n' - ), - }, - expected_kind="preserved_namespace", - expected_error="Seed 'countries' has no logical schema", - ), ], ids=lambda case: case.description, ) -def test_given_projects_python_rejects_when_assembling_natively_then_raises_python_error( +def test_given_model_sql_the_parser_rejects_when_assembling_then_raises_python_error( test_case: AssemblyDeferralTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - record_dir: Path = tmp_path / "records" - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) inputs: CompileProjectInputs = project_inputs(project_dir=tmp_path, files=test_case.files) - with pytest.raises(CompileInputError) as python_error: - _ = assemble_with( - inputs=inputs, - engine=CompilerEngine.NATIVE, - monkeypatch=monkeypatch, - deferred=DEFERRED_PROJECT_FACTS, - ) - with pytest.raises(CompileInputError) as native_error: - _ = assemble_with( - inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch - ) - - assert test_case.expected_error in str(python_error.value) - assert str(native_error.value) == str(python_error.value) - assert assembly_deferrals(record_dir) == Counter( - f"project_assembly:{kind}" for kind in filter(None, (test_case.expected_kind,)) - ) - - -@pytest.mark.parametrize( - "test_case", - [ - DeferredAssemblyTestCase( - description="a seed schema reading a list variable", - effective_vars={"tags": ["daily"]}, - seed_schema="${tags}", - expected_schema="['daily']", - expected_kind="target_template", - ) - ], - ids=lambda case: case.description, -) -def test_given_a_variable_only_python_renders_when_assembling_natively_then_python_assembles( - test_case: DeferredAssemblyTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - record_dir: Path = tmp_path / "records" - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) - inputs: CompileProjectInputs = replace( - project_inputs( - project_dir=tmp_path, - files={ - "sqlbuild_project.toml": _PROJECT_TOML, - "seeds/lookups.yml": seed_yml(test_case.seed_schema), - **_SEED_FILES, - }, - ), - effective_vars=test_case.effective_vars, - ) - - python: CompiledProject = assemble_with( - inputs=inputs, - engine=CompilerEngine.NATIVE, - monkeypatch=monkeypatch, - deferred=DEFERRED_PROJECT_FACTS, - ) - native: CompiledProject = assemble_with( - inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch - ) + with pytest.raises(CompileInputError) as raised: + _ = assemble_with(inputs=inputs, engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch) - assert assembly_view(native) == assembly_view(python) - assert native.seeds[0].destination.schema == test_case.expected_schema - assert assembly_deferrals(record_dir) == Counter( - {f"project_assembly:{test_case.expected_kind}": 1} - ) + assert test_case.expected_error in str(raised.value) @pytest.mark.parametrize( @@ -269,9 +207,7 @@ def test_given_a_variable_only_python_renders_when_assembling_natively_then_pyth def test_given_windows_environment_when_assembling_natively_then_env_lookups_match_python( test_case: WindowsEnvironmentTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - record_dir: Path = tmp_path / "records" monkeypatch.setattr(os, "environ", windows_environ(test_case.environment)) - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) inputs: CompileProjectInputs = project_inputs( project_dir=tmp_path, files={ @@ -281,19 +217,11 @@ def test_given_windows_environment_when_assembling_natively_then_env_lookups_mat }, ) - python: CompiledProject = assemble_with( - inputs=inputs, - engine=CompilerEngine.NATIVE, - monkeypatch=monkeypatch, - deferred=DEFERRED_PROJECT_FACTS, - ) native: CompiledProject = assemble_with( - inputs=inputs, engine=CompilerEngine.NATIVE_PREVIEW, monkeypatch=monkeypatch + inputs=inputs, engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch ) - assert python.seeds[0].destination.schema == test_case.expected_schema - assert assembly_view(native) == assembly_view(python) - assert assembly_deferrals(record_dir) == Counter() + assert native.seeds[0].destination.schema == test_case.expected_schema @pytest.mark.parametrize( @@ -310,7 +238,6 @@ def test_given_windows_environment_when_assembling_natively_then_env_lookups_mat ), }, expected_rejected={"broken_hook": False, "broken_query": False, "parses": True}, - expected_python_validations=7, ) ], ids=lambda case: case.description, @@ -318,27 +245,16 @@ def test_given_windows_environment_when_assembling_natively_then_env_lookups_mat def test_given_sql_analysis_opt_outs_when_attaching_natively_then_python_rejections_match( test_case: OptOutTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - record_dir: Path = tmp_path / "records" - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) files: dict[str, str] = {"sqlbuild_project.toml": _REQUIRED_ANALYSIS_TOML, **test_case.models} - python: tuple[dict[str, bool], int] = rejected_opt_outs( - project_dir=tmp_path / "python", - files=files, - engine=CompilerEngine.NATIVE, - monkeypatch=monkeypatch, - deferred=DEFERRED_SYNTAX_CHECK, - ) - native: tuple[dict[str, bool], int] = rejected_opt_outs( + native: dict[str, bool] = rejected_opt_outs( project_dir=tmp_path / "native", files=files, - engine=CompilerEngine.NATIVE_PREVIEW, + engine=CompilerEngine.NATIVE, monkeypatch=monkeypatch, ) - assert python == (test_case.expected_rejected, test_case.expected_python_validations) - assert native == (test_case.expected_rejected, 0) - assert assembly_deferrals(record_dir) == Counter() + assert native == test_case.expected_rejected @pytest.mark.parametrize( @@ -427,8 +343,6 @@ def test_given_project_when_assembling_natively_then_request_carries_graph_facts def test_given_native_assembly_panics_when_assembling_then_raises_without_python_retry( test_case: NativePanicTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - record_dir: Path = tmp_path / "records" - monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE.value) inputs: CompileProjectInputs = project_inputs( project_dir=tmp_path / "project", files=test_case.files @@ -441,8 +355,4 @@ def test_given_native_assembly_panics_when_assembling_then_raises_without_python inference_profile=ExpressionInferenceProfile(sql_analysis_dialect=test_case.dialect), ) - assert (str(raised.value), calls, assembly_deferrals(record_dir)) == ( - test_case.expected_message, - Counter(), - Counter(), - ) + assert (str(raised.value), calls) == (test_case.expected_message, Counter()) diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py index 8a5e4b9279..2419372f0b 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/_test_types.py @@ -455,30 +455,6 @@ class ExtractSqlAnalysisExpectedBranchesErrorTestCase: expected_error_fragment: str -@dataclass(frozen=True) -class ModelBuildDepsTestCase: - description: str - references: tuple[CompileSqlReference, ...] - expected_deps: tuple[CompiledObjectKey, ...] - seed_names: frozenset[str] = field(default_factory=frozenset) - - -@dataclass(frozen=True) -class AuditScopeDepsTestCase: - description: str - references: tuple[CompileSqlReference, ...] - attached_target_kind: str | None - attached_target_name: str | None - expected_scope_deps: tuple[CompiledObjectKey, ...] - - -@dataclass(frozen=True) -class SqlTestScopeDepsTestCase: - description: str - expected_model_names: tuple[str, ...] - expected_scope_deps: tuple[CompiledObjectKey, ...] - - @dataclass(frozen=True) class AssembleCompiledProjectTestCase: description: str diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_deps.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_deps.py deleted file mode 100644 index 749109ef72..0000000000 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_deps.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -import pytest - -from sqlbuild.compiler.compile._helpers.deps.dependencies import ( - audit_scope_deps, - model_build_deps, - sql_test_scope_deps, -) -from sqlbuild.compiler.compile.models import ( - CompiledObjectKey, - CompileSqlReference, -) -from sqlbuild.compiler.compile.types import CompiledResourceType -from sqlbuild.compiler.references.types import SqlReferenceKind -from tests.unit.src.sqlbuild.compiler.compile._helpers._test_types import ( - AuditScopeDepsTestCase, - ModelBuildDepsTestCase, - SqlTestScopeDepsTestCase, -) - - -@pytest.mark.parametrize( - "test_case", - [ - ModelBuildDepsTestCase( - description="maps model source and dbt references to typed build deps", - references=( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="stg_orders"), - CompileSqlReference(ref_kind=SqlReferenceKind.SOURCE, ref_name="raw_orders"), - CompileSqlReference(ref_kind=SqlReferenceKind.DBT_REF, ref_name="dbt_customers"), - CompileSqlReference( - ref_kind=SqlReferenceKind.TABLE_FUNCTION, - ref_name="customer_orders", - ), - ), - expected_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="stg_orders"), - CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name="raw_orders"), - CompiledObjectKey(resource_type=CompiledResourceType.DBT_REF, name="dbt_customers"), - CompiledObjectKey( - resource_type=CompiledResourceType.TABLE_FN, - name="customer_orders", - ), - ), - ), - ModelBuildDepsTestCase( - description="dedupes repeated model deps while preserving first seen order", - references=( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="stg_orders"), - CompileSqlReference(ref_kind=SqlReferenceKind.SOURCE, ref_name="raw_orders"), - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="stg_orders"), - ), - expected_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="stg_orders"), - CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name="raw_orders"), - ), - ), - ModelBuildDepsTestCase( - description="seed reference produces a SEED typed dep key", - references=( - CompileSqlReference(ref_kind=SqlReferenceKind.REF, ref_name="stg_orders"), - CompileSqlReference(ref_kind=SqlReferenceKind.SEED, ref_name="waffle_types"), - ), - expected_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="stg_orders"), - CompiledObjectKey(resource_type=CompiledResourceType.SEED, name="waffle_types"), - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_sql_references_when_deriving_model_build_deps_then_returns_typed_object_keys( - test_case: ModelBuildDepsTestCase, -) -> None: - deps: tuple[CompiledObjectKey, ...] = model_build_deps( - references=test_case.references, seed_names=test_case.seed_names - ) - - assert deps == test_case.expected_deps - - -@pytest.mark.parametrize( - "test_case", - [ - AuditScopeDepsTestCase( - description="combines audit references with attached model target", - references=( - CompileSqlReference(ref_kind=SqlReferenceKind.SOURCE, ref_name="raw_orders"), - ), - attached_target_kind="model", - attached_target_name="orders", - expected_scope_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name="raw_orders"), - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="orders"), - ), - ), - AuditScopeDepsTestCase( - description="dedupes attached source target already present in refs", - references=( - CompileSqlReference(ref_kind=SqlReferenceKind.SOURCE, ref_name="raw_orders"), - ), - attached_target_kind="source", - attached_target_name="raw_orders", - expected_scope_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.SOURCE, name="raw_orders"), - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_audit_refs_and_target_when_deriving_scope_deps_then_returns_scope_keys( - test_case: AuditScopeDepsTestCase, -) -> None: - deps: tuple[CompiledObjectKey, ...] = audit_scope_deps( - references=test_case.references, - attached_target_kind=test_case.attached_target_kind, - attached_target_name=test_case.attached_target_name, - ) - - assert deps == test_case.expected_scope_deps - - -@pytest.mark.parametrize( - "test_case", - [ - SqlTestScopeDepsTestCase( - description="uses expected models as sql test scope deps", - expected_model_names=("orders", "daily_revenue"), - expected_scope_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="orders"), - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="daily_revenue"), - ), - ), - SqlTestScopeDepsTestCase( - description="dedupes repeated expected model names", - expected_model_names=("orders", "orders"), - expected_scope_deps=( - CompiledObjectKey(resource_type=CompiledResourceType.MODEL, name="orders"), - ), - ), - ], - ids=lambda case: case.description, -) -def test_given_expected_models_when_deriving_sql_test_scope_deps_then_returns_model_keys( - test_case: SqlTestScopeDepsTestCase, -) -> None: - deps: tuple[CompiledObjectKey, ...] = sql_test_scope_deps( - expected_model_names=test_case.expected_model_names - ) - - assert deps == test_case.expected_scope_deps diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py index 5247a14641..2f1ce465f5 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py @@ -20,16 +20,16 @@ NativeFallbackRecorderTestCase( description="counts_per_site_and_kind_sorted", recorded=( - (NativeFallbackSite.PROJECT_ASSEMBLY, "deferred"), + (NativeFallbackSite.SCOPE_REBIND_LOOKUP, "deferred"), (NativeFallbackSite.MACRO_CALL_MOCKED, "deferred"), - (NativeFallbackSite.PROJECT_ASSEMBLY, "deferred"), - (NativeFallbackSite.PROJECT_ASSEMBLY, "error"), + (NativeFallbackSite.SCOPE_REBIND_LOOKUP, "deferred"), + (NativeFallbackSite.SCOPE_REBIND_LOOKUP, "error"), ), expected_files=[ [ + ["declaration_scopes.rebind_lookup", "deferred", 2], + ["declaration_scopes.rebind_lookup", "error", 1], ["macro_calls.mocked_evaluation", "deferred", 1], - ["project_assembly.assembly", "deferred", 2], - ["project_assembly.assembly", "error", 1], ] ], ), diff --git a/tests/unit/src/sqlbuild/compiler/planner/_helpers/helpers.py b/tests/unit/src/sqlbuild/compiler/planner/_helpers/helpers.py index bfc3e44d74..98b314f050 100644 --- a/tests/unit/src/sqlbuild/compiler/planner/_helpers/helpers.py +++ b/tests/unit/src/sqlbuild/compiler/planner/_helpers/helpers.py @@ -10,10 +10,10 @@ from types import MappingProxyType from typing import ClassVar +import sqlbuild._native as _native from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapter.contract.models import RelationInfo from sqlbuild.compiler.auditing.types import AuditSeverity -from sqlbuild.compiler.compile._helpers.deps.dependencies import audit_scope_deps from sqlbuild.compiler.compile._helpers.sql_tests.core import ( extract_assertion_target_model_names, ) @@ -1378,7 +1378,7 @@ def build_scheduling_audit( return CompiledAudit( key=CompiledObjectKey(resource_type=CompiledResourceType.AUDIT, name=name), definition_name="test_audit", - scope_deps=audit_scope_deps( + scope_deps=native_audit_scope_deps( references=references, attached_target_kind=attached_target_kind, attached_target_name=attached_target_name, @@ -1448,3 +1448,31 @@ def build_seed_identity_compiled_seed( qualified_name="main.orders", ), ) + + +def native_audit_scope_deps( + *, + references: tuple[CompileSqlReference, ...], + attached_target_kind: AttachedAuditTargetKind | None, + attached_target_name: str | None, +) -> tuple[CompiledObjectKey, ...]: + """The audit's scope dependencies as native project assembly derives them.""" + + attached: tuple[str, str] | None = { + True: None, + False: (str(attached_target_kind), str(attached_target_name)), + }[attached_target_kind is None or attached_target_name is None] + audit_row: tuple[list[tuple[str, str, str | None]], tuple[str, str] | None] = ( + [ + (str(reference.ref_kind), reference.ref_name, reference.ref_package) + for reference in references + ], + attached, + ) + audit_deps: list[list[tuple[str, str]]] = _native.assemble_project_resource_facts( + ("generic", None, (None, None, None, None), [], [], [], [], [], [], [audit_row]) + )[4] + return tuple( + CompiledObjectKey(resource_type=CompiledResourceType(kind), name=name) + for kind, name in audit_deps[0] + ) From 61ac7808e28142f83b0c481bd232616049e6df84 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:47:50 +0100 Subject: [PATCH 33/36] feat(native): scan SQL test and scenario CTEs natively for every consumer Relationships, test CTE extraction and scenario expected names now use the native top-level CTE scanner, which classifies identifier characters and upper-cases keywords with Python's own string tables. The Python scanner and its dead identifier helpers are deleted; goldens record the pre-port Python outcomes, including Unicode identifier edges. --- .../relationship_names/_helpers/cte_scan.rs | 10 +- .../tests/test_expected_model_names.rs | 10 + .../compile/_helpers/scenarios/core.py | 39 +- .../compile/_helpers/sql_tests/core.py | 308 +- .../sql_analysis/_helpers/scanning.py | 56 - .../main/_is_identifier_character.py | 9 - .../sql_analysis/main/_is_identifier_start.py | 9 - ...hip_scan_backslash_triple_raw_dialect.json | 12002 ++++++++++++++++ ...lationship_scan_escape_prefix_dialect.json | 12002 ++++++++++++++++ .../relationship_scan_generic_dialect.json | 12002 ++++++++++++++++ ...tionship_scan_nested_comments_dialect.json | 12002 ++++++++++++++++ ...ationship_scan_slash_comments_dialect.json | 12002 ++++++++++++++++ ..._unicode_identifier_and_keyword_edges.json | 706 + .../sqlbuild/compiler/scopes/_test_types.py | 15 +- .../src/sqlbuild/compiler/scopes/helpers.py | 119 +- .../scopes/test_native_relationship_names.py | 66 +- 16 files changed, 60880 insertions(+), 477 deletions(-) delete mode 100644 src/sqlbuild/compiler/sql_analysis/main/_is_identifier_character.py delete mode 100644 src/sqlbuild/compiler/sql_analysis/main/_is_identifier_start.py create mode 100644 tests/goldens/python_oracle/relationship_scan_backslash_triple_raw_dialect.json create mode 100644 tests/goldens/python_oracle/relationship_scan_escape_prefix_dialect.json create mode 100644 tests/goldens/python_oracle/relationship_scan_generic_dialect.json create mode 100644 tests/goldens/python_oracle/relationship_scan_nested_comments_dialect.json create mode 100644 tests/goldens/python_oracle/relationship_scan_slash_comments_dialect.json create mode 100644 tests/goldens/python_oracle/relationship_scan_unicode_identifier_and_keyword_edges.json diff --git a/crates/sqlbuild-scopes/src/relationship_names/_helpers/cte_scan.rs b/crates/sqlbuild-scopes/src/relationship_names/_helpers/cte_scan.rs index e633b46d43..b2c9ec8164 100644 --- a/crates/sqlbuild-scopes/src/relationship_names/_helpers/cte_scan.rs +++ b/crates/sqlbuild-scopes/src/relationship_names/_helpers/cte_scan.rs @@ -1,7 +1,11 @@ //! The top-level CTE scan of `extract_top_level_ctes_with_scanner`. +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::is_python_alnum::is_python_alnum; +use sqlbuild_core::text::main::is_python_alpha::is_python_alpha; use sqlbuild_core::text::main::is_python_space::is_python_space; use sqlbuild_core::text::main::python_strip::python_strip; +use sqlbuild_core::text::main::python_upper::python_upper; use sqlbuild_sqltext::sql_scan::main::dialect_matching_paren::dialect_matching_paren; use sqlbuild_sqltext::sql_scan::main::dialect_non_code_end::dialect_non_code_end; use sqlbuild_sqltext::sql_scan::models::{LexicalSyntax, Unclosed}; @@ -232,7 +236,7 @@ fn try_consume_keyword(sql: &str, start: usize, keyword: &str) -> Option .char_indices() .nth(keyword.len()) .map_or(sql.len(), |(offset, _)| start + offset); - if sql[start..window_end].to_uppercase() != keyword { + if python_upper(active_python_text(), &sql[start..window_end]) != keyword { return None; } let continues = |character: Option| character.is_some_and(is_identifier_character); @@ -245,7 +249,7 @@ fn try_consume_keyword(sql: &str, start: usize, keyword: &str) -> Option fn read_identifier<'sql>(text: &ScanText<'sql>, start: usize) -> Scan<(&'sql str, usize)> { let mut characters = text.sql[start..].char_indices(); match characters.next() { - Some((_, first)) if first.is_alphabetic() || first == '_' => {} + Some((_, first)) if is_python_alpha(active_python_text(), first) || first == '_' => {} _ => return Err(failed(text, "expected a CTE name".to_owned())), } let end: usize = characters @@ -260,5 +264,5 @@ fn matching_paren(text: &ScanText<'_>, open: usize, syntax: &LexicalSyntax) -> S /// Python's `isalnum() or _`. fn is_identifier_character(character: char) -> bool { - character.is_alphanumeric() || character == '_' + is_python_alnum(active_python_text(), character) || character == '_' } diff --git a/crates/sqlbuild-scopes/src/relationship_names/tests/test_expected_model_names.rs b/crates/sqlbuild-scopes/src/relationship_names/tests/test_expected_model_names.rs index bb640b410b..60d150c7a0 100644 --- a/crates/sqlbuild-scopes/src/relationship_names/tests/test_expected_model_names.rs +++ b/crates/sqlbuild-scopes/src/relationship_names/tests/test_expected_model_names.rs @@ -26,6 +26,16 @@ fn given_sql_test_when_scanning_expected_models_then_names_and_errors_match_pyth sql: "WITH __expected__a AS (SELECT 1) select 1 ; -- done\n", expected_outcome: scanned(&["a"]), }, + ExpectedNamesTestCase { + description: "an alphabetic mark Python's isalpha rejects does not start a CTE name", + sql: "WITH \u{345}x AS (SELECT 1)", + expected_outcome: failed("SQL test 'tests/unit/test_orders.sql' expected a CTE name"), + }, + ExpectedNamesTestCase { + description: "an alphabetic mark Python's isalnum rejects ends a CTE name", + sql: "WITH __expected__a\u{345} AS (SELECT 1)", + expected_outcome: failed("SQL test 'tests/unit/test_orders.sql' expected keyword AS"), + }, ExpectedNamesTestCase { description: "no top-level WITH has no expected models", sql: "SELECT 1", diff --git a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py index 1cc9d267a3..fb37e844cc 100644 --- a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py @@ -7,16 +7,7 @@ import orjson import sqlbuild._native as _native -from sqlbuild.compiler.compile._helpers.sql_tests.core import ( - _require_prefixed_name, - _skip_ignorable, - _try_consume_keyword, - extract_top_level_ctes_with_scanner, -) -from sqlbuild.compiler.compile.constants import ( - EXPECTED_TEST_CTE_PREFIX, - SQL_WITH_KEYWORD, -) +from sqlbuild.compiler.compile._helpers.sql_tests.core import scanned_expected_model_names from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( CompileSqlScenarioCte, @@ -24,11 +15,6 @@ ) from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -_CONTEXT: str = "SQL scenario" -_WITH_REQUIREMENT: str = ( - "fixture CTEs and at least one __expected__ or __assert__ CTE" -) - def extract_sql_scenario_ctes( *, sql: str, file_label: str, syntax: SqlLexicalSyntax @@ -64,25 +50,6 @@ def extract_sql_scenario_expected_model_names( ) -> tuple[str, ...]: """Extract explicit expected-model relationships without inspecting CTE bodies.""" - start: int = _skip_ignorable(sql=sql, start=0, context_label=_CONTEXT, syntax=syntax) - if _try_consume_keyword(sql=sql, start=start, keyword=SQL_WITH_KEYWORD) is None: - return () - ctes: tuple[CompileSqlScenarioCte, ...] = extract_top_level_ctes_with_scanner( - sql=sql, - file_label=file_label, - context_label=_CONTEXT, - with_requirement=_WITH_REQUIREMENT, - cte_type=CompileSqlScenarioCte, - syntax=syntax, - ) - return tuple( - _require_prefixed_name( - cte_name=cte.name, - prefix=EXPECTED_TEST_CTE_PREFIX, - label="__expected__", - file_label=file_label, - context_label=_CONTEXT, - ) - for cte in ctes - if cte.name.startswith(EXPECTED_TEST_CTE_PREFIX) + return scanned_expected_model_names( + sql=sql, file_label=file_label, scenario=True, syntax=syntax ) diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py index c76b043fa5..f9e2f06b19 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py @@ -2,21 +2,13 @@ from __future__ import annotations -from collections.abc import Callable from functools import lru_cache import sqlbuild._native as _native from sqlbuild.compiler.compile._helpers.refs.references import extract_sql_references from sqlbuild.compiler.compile.constants import ( DEFAULT_SQL_TEST_MODE, - EXPECTED_TEST_CTE_PREFIX, OMITTED_CEREMONIAL_SELECT_SQL, - SQL_ARGUMENT_SEPARATOR_TOKEN, - SQL_CEREMONIAL_SELECT_VALUE, - SQL_OPEN_PAREN_TOKEN, - SQL_QUOTE_TOKENS, - SQL_STATEMENT_TERMINATOR_TOKEN, - SQL_WITH_KEYWORD, ) from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( @@ -25,33 +17,21 @@ ) from sqlbuild.compiler.compile.types import SqlTestMode from sqlbuild.compiler.references.types import SqlReferenceKind -from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren -from sqlbuild.compiler.sql_analysis.main._is_identifier_character import ( - is_identifier_character, -) -from sqlbuild.compiler.sql_analysis.main._is_identifier_start import is_identifier_start -from sqlbuild.compiler.sql_analysis.main._skip_dialect_non_code import dialect_non_code_end from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -_CONTEXT: str = "SQL test" -_SQL_TEST_WITH_REQUIREMENT: str = "mock CTEs and one __expected__ CTE" -_MATERIALIZED_KEYWORD: str = "MATERIALIZED" -_NOT_KEYWORD: str = "NOT" - +@lru_cache(maxsize=4096) def extract_unclassified_sql_test_ctes( *, sql: str, file_label: str, syntax: SqlLexicalSyntax ) -> tuple[CompileSqlTestCte, ...]: """Extract raw top-level CTEs before mode-specific classification.""" - return extract_top_level_ctes_with_scanner( - sql=sql, - file_label=file_label, - context_label=_CONTEXT, - with_requirement=_SQL_TEST_WITH_REQUIREMENT, - cte_type=CompileSqlTestCte, - syntax=syntax, - ) + error: str | None + ctes: list[tuple[str, str]] + error, ctes = _native.scope_test_ctes([(sql, file_label)], syntax.native_mapping)[0] + if error is not None: + raise CompileInputError(error) + return tuple(CompileSqlTestCte(name=name, sql_body=body) for name, body in ctes) def extract_sql_test_expected_model_names( @@ -65,29 +45,26 @@ def extract_sql_test_expected_model_names( if mode is not SqlTestMode.MODEL: return () - start: int = _skip_ignorable(sql=sql, start=0, syntax=syntax) - if _try_consume_keyword(sql=sql, start=start, keyword=SQL_WITH_KEYWORD) is None: - return () - ctes: tuple[CompileSqlTestCte, ...] = extract_top_level_ctes_with_scanner( - sql=sql, - file_label=file_label, - context_label=_CONTEXT, - with_requirement=_SQL_TEST_WITH_REQUIREMENT, - cte_type=CompileSqlTestCte, - syntax=syntax, - ) - return tuple( - _require_prefixed_name( - cte_name=cte.name, - prefix=EXPECTED_TEST_CTE_PREFIX, - label="__expected__", - file_label=file_label, - ) - for cte in ctes - if cte.name.startswith(EXPECTED_TEST_CTE_PREFIX) + return scanned_expected_model_names( + sql=sql, file_label=file_label, scenario=False, syntax=syntax ) +def scanned_expected_model_names( + *, sql: str, file_label: str, scenario: bool, syntax: SqlLexicalSyntax +) -> tuple[str, ...]: + """A SQL test's or scenario's `__expected__` names, raising the scan's error.""" + + error: str | None + names: list[str] + error, names = _native.scope_expected_model_names( + [(sql, file_label)], scenario, syntax.native_mapping + )[0] + if error is not None: + raise CompileInputError(error) + return tuple(names) + + def extract_assertion_target_model_names( *, assertion_sql: tuple[str, ...], @@ -106,37 +83,6 @@ def extract_assertion_target_model_names( return tuple(dict.fromkeys(targets)) -@lru_cache(maxsize=4096) -def extract_top_level_ctes_with_scanner[CteT]( - *, - sql: str, - file_label: str, - context_label: str, - with_requirement: str, - cte_type: Callable[..., CteT], - syntax: SqlLexicalSyntax, -) -> tuple[CteT, ...]: - """Scan top-level `WITH` CTEs, optionally followed by the ceremonial `SELECT 1`.""" - - ctes: tuple[tuple[str, str], ...] - index: int - ctes, index = _scan_top_level_ctes( - sql=sql, - file_label=file_label, - context_label=context_label, - with_requirement=with_requirement, - syntax=syntax, - ) - _validate_ceremonial_select( - sql=sql, - start=index, - file_label=file_label, - context_label=context_label, - syntax=syntax, - ) - return tuple(cte_type(name=name, sql_body=body) for name, body in ctes) - - def complete_omitted_ceremonial_select(*, sql: str, syntax: SqlLexicalSyntax) -> str: """Return test or scenario SQL as one statement, adding an omitted trailing `SELECT 1`.""" @@ -144,209 +90,3 @@ def complete_omitted_ceremonial_select(*, sql: str, syntax: SqlLexicalSyntax) -> if offset is None: return sql return f"{sql[:offset]}{OMITTED_CEREMONIAL_SELECT_SQL}{sql[offset:]}" - - -def _scan_top_level_ctes( - *, - sql: str, - file_label: str, - context_label: str, - with_requirement: str, - syntax: SqlLexicalSyntax, -) -> tuple[tuple[tuple[str, str], ...], int]: - """Return top-level CTEs and the first code position after them.""" - - with_end: int | None = _try_consume_keyword( - sql=sql, - start=_skip_ignorable(sql=sql, start=0, context_label=context_label, syntax=syntax), - keyword=SQL_WITH_KEYWORD, - ) - if with_end is None: - raise CompileInputError( - f"{context_label} '{file_label}' must declare {with_requirement} in a top-level " - "WITH clause" - ) - index: int = _skip_ignorable( - sql=sql, start=with_end, context_label=context_label, syntax=syntax - ) - recursive_end: int | None = _try_consume_keyword(sql=sql, start=index, keyword="RECURSIVE") - if recursive_end is not None: - index = _skip_ignorable( - sql=sql, start=recursive_end, context_label=context_label, syntax=syntax - ) - - ctes: list[tuple[str, str]] = [] - seen_cte_names: set[str] = set() - while True: - cte_name, index = _read_identifier( - sql=sql, start=index, file_label=file_label, context_label=context_label - ) - if cte_name in seen_cte_names: - raise CompileInputError( - f"{context_label} '{file_label}' defines duplicate CTE '{cte_name}'" - ) - seen_cte_names.add(cte_name) - - index = _skip_ignorable(sql=sql, start=index, context_label=context_label, syntax=syntax) - if index < len(sql) and sql[index] == SQL_OPEN_PAREN_TOKEN: - index = ( - find_matching_paren( - sql=sql, open_paren_index=index, context=context_label, syntax=syntax - ) - + 1 - ) - index = _skip_ignorable( - sql=sql, start=index, context_label=context_label, syntax=syntax - ) - index = _consume_keyword( - sql=sql, - start=index, - keyword="AS", - file_label=file_label, - context_label=context_label, - ) - index = _skip_ignorable(sql=sql, start=index, context_label=context_label, syntax=syntax) - hint: str | None = _materialization_hint( - sql=sql, start=index, context_label=context_label, syntax=syntax - ) - if hint is not None: - raise CompileInputError( - f"{context_label} '{file_label}' CTE '{cte_name}' must not use AS {hint}; " - f"materialization hints are not supported in {context_label} CTEs" - ) - if index >= len(sql) or sql[index] != SQL_OPEN_PAREN_TOKEN: - raise CompileInputError( - f"{context_label} '{file_label}' CTE '{cte_name}' must use AS (...)" - ) - cte_body_start: int = index + 1 - cte_body_end: int = find_matching_paren( - sql=sql, open_paren_index=index, context=context_label, syntax=syntax - ) - ctes.append((cte_name, sql[cte_body_start:cte_body_end].strip())) - index = _skip_ignorable( - sql=sql, start=cte_body_end + 1, context_label=context_label, syntax=syntax - ) - if index < len(sql) and sql[index] == SQL_ARGUMENT_SEPARATOR_TOKEN: - index = _skip_ignorable( - sql=sql, start=index + 1, context_label=context_label, syntax=syntax - ) - continue - return tuple(ctes), index - - -def _materialization_hint( - *, sql: str, start: int, context_label: str, syntax: SqlLexicalSyntax -) -> str | None: - if _try_consume_keyword(sql=sql, start=start, keyword=_MATERIALIZED_KEYWORD) is not None: - return _MATERIALIZED_KEYWORD - not_end: int | None = _try_consume_keyword(sql=sql, start=start, keyword=_NOT_KEYWORD) - if not_end is None: - return None - index: int = _skip_ignorable(sql=sql, start=not_end, context_label=context_label, syntax=syntax) - if _try_consume_keyword(sql=sql, start=index, keyword=_MATERIALIZED_KEYWORD) is None: - return None - return f"{_NOT_KEYWORD} {_MATERIALIZED_KEYWORD}" - - -def _require_prefixed_name( - *, cte_name: str, prefix: str, label: str, file_label: str, context_label: str = _CONTEXT -) -> str: - extracted_name: str = cte_name.removeprefix(prefix) - if extracted_name: - return extracted_name - raise CompileInputError(f"{context_label} '{file_label}' must use {label} to identify a target") - - -def _validate_ceremonial_select( - *, - sql: str, - start: int, - file_label: str, - syntax: SqlLexicalSyntax, - context_label: str = _CONTEXT, -) -> None: - if _is_statement_end( - sql=sql, start=start, context_label=context_label, syntax=syntax - ) or _is_ceremonial_select_statement( - sql=sql, start=start, context_label=context_label, syntax=syntax - ): - return - raise CompileInputError( - f"{context_label} '{file_label}' must end after its CTEs; only an optional " - "ceremonial top-level `SELECT 1` may follow them" - ) - - -def _is_ceremonial_select_statement( - *, sql: str, start: int, syntax: SqlLexicalSyntax, context_label: str = _CONTEXT -) -> bool: - index: int = _skip_ignorable(sql=sql, start=start, context_label=context_label, syntax=syntax) - select_end: int | None = _try_consume_keyword(sql=sql, start=index, keyword="SELECT") - if select_end is None: - return False - index = _skip_ignorable(sql=sql, start=select_end, context_label=context_label, syntax=syntax) - if index >= len(sql) or sql[index] != SQL_CEREMONIAL_SELECT_VALUE: - return False - index = _skip_ignorable(sql=sql, start=index + 1, context_label=context_label, syntax=syntax) - return _is_statement_end(sql=sql, start=index, context_label=context_label, syntax=syntax) - - -def _is_statement_end( - *, sql: str, start: int, syntax: SqlLexicalSyntax, context_label: str = _CONTEXT -) -> bool: - index: int = start - if index < len(sql) and sql[index] == SQL_STATEMENT_TERMINATOR_TOKEN: - index = _skip_ignorable( - sql=sql, start=index + 1, context_label=context_label, syntax=syntax - ) - return index == len(sql) - - -def _consume_keyword( - *, sql: str, start: int, keyword: str, file_label: str, context_label: str = _CONTEXT -) -> int: - keyword_end: int | None = _try_consume_keyword(sql=sql, start=start, keyword=keyword) - if keyword_end is not None: - return keyword_end - raise CompileInputError(f"{context_label} '{file_label}' expected keyword {keyword}") - - -def _try_consume_keyword(*, sql: str, start: int, keyword: str) -> int | None: - keyword_end: int = start + len(keyword) - if sql[start:keyword_end].upper() != keyword: - return None - if keyword_end < len(sql) and is_identifier_character(sql[keyword_end]): - return None - if start > 0 and is_identifier_character(sql[start - 1]): - return None - return keyword_end - - -def _read_identifier( - *, sql: str, start: int, file_label: str, context_label: str = _CONTEXT -) -> tuple[str, int]: - if start >= len(sql) or not is_identifier_start(sql[start]): - raise CompileInputError(f"{context_label} '{file_label}' expected a CTE name") - index: int = start + 1 - while index < len(sql) and is_identifier_character(sql[index]): - index += 1 - return sql[start:index], index - - -def _skip_ignorable( - *, sql: str, start: int, syntax: SqlLexicalSyntax, context_label: str = _CONTEXT -) -> int: - index: int = start - while index < len(sql): - if sql[index].isspace(): - index += 1 - continue - if sql[index] not in SQL_QUOTE_TOKENS: - comment_end: int | None = dialect_non_code_end( - sql=sql, start=index, syntax=syntax, context=context_label - ) - if comment_end is not None: - index = comment_end - continue - return index - return index diff --git a/src/sqlbuild/compiler/sql_analysis/_helpers/scanning.py b/src/sqlbuild/compiler/sql_analysis/_helpers/scanning.py index e88be3e72d..b1a580421b 100644 --- a/src/sqlbuild/compiler/sql_analysis/_helpers/scanning.py +++ b/src/sqlbuild/compiler/sql_analysis/_helpers/scanning.py @@ -231,50 +231,6 @@ def iter_code_positions_impl( index += 1 -def skip_ignorable_impl( - *, sql: str, start: int, context: str = "SQL", syntax: SqlLexicalSyntax | None = None -) -> int: - """Return the first offset at or after `start` that is not whitespace or a comment.""" - - index: int = start - while index < len(sql): - if sql[index].isspace(): - index += 1 - continue - if sql[index] in SQL_TEXT_START_CHARACTERS: - return index - comment_end: int | None = _non_code_end( - sql=sql, index=index, context=context, syntax=syntax - ) - if comment_end is None: - return index - index = comment_end - return index - - -def strip_ignorable_impl( - *, sql: str, context: str = "SQL", syntax: SqlLexicalSyntax | None = None -) -> str: - """Return the SQL without leading or trailing whitespace and comments.""" - - code_start: int = skip_ignorable_impl(sql=sql, start=0, context=context, syntax=syntax) - code_end: int = code_start - index: int = code_start - while index < len(sql): - non_code_end: int | None = _non_code_end( - sql=sql, index=index, context=context, syntax=syntax - ) - if non_code_end is None: - index += 1 - if not sql[index - 1].isspace(): - code_end = index - continue - if sql[index] in SQL_TEXT_START_CHARACTERS: - code_end = non_code_end - index = non_code_end - return sql[code_start:code_end] - - def _non_code_end( *, sql: str, index: int, context: str, syntax: SqlLexicalSyntax | None ) -> int | None: @@ -287,15 +243,3 @@ def _non_code_end( if sql[index] in SQL_TEXT_START_CHARACTERS: return quoted_text_end_impl(sql=sql, start=index, context=context) return None - - -def is_identifier_start_impl(character: str) -> bool: - """Return whether a character can begin an SQL identifier.""" - - return character.isalpha() or character == SQL_IDENTIFIER_PREFIX - - -def is_identifier_character_impl(character: str) -> bool: - """Return whether a character can continue an SQL identifier.""" - - return character.isalnum() or character == SQL_IDENTIFIER_PREFIX diff --git a/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_character.py b/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_character.py deleted file mode 100644 index 69f0707cb9..0000000000 --- a/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_character.py +++ /dev/null @@ -1,9 +0,0 @@ -"""SQL identifier character classification entrypoint.""" - -from sqlbuild.compiler.sql_analysis._helpers.scanning import is_identifier_character_impl - - -def is_identifier_character(character: str) -> bool: - """Return whether a character can continue an SQL identifier.""" - - return is_identifier_character_impl(character) diff --git a/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_start.py b/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_start.py deleted file mode 100644 index feb399f26f..0000000000 --- a/src/sqlbuild/compiler/sql_analysis/main/_is_identifier_start.py +++ /dev/null @@ -1,9 +0,0 @@ -"""SQL identifier start classification entrypoint.""" - -from sqlbuild.compiler.sql_analysis._helpers.scanning import is_identifier_start_impl - - -def is_identifier_start(character: str) -> bool: - """Return whether a character can begin an SQL identifier.""" - - return is_identifier_start_impl(character) diff --git a/tests/goldens/python_oracle/relationship_scan_backslash_triple_raw_dialect.json b/tests/goldens/python_oracle/relationship_scan_backslash_triple_raw_dialect.json new file mode 100644 index 0000000000..30c61e79b5 --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_backslash_triple_raw_dialect.json @@ -0,0 +1,12002 @@ +[ +[ +"relationship_scan", +"8447c5b3d02d60ba962f5b9e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"20fd14152c40e79bab186d6d" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17a892812df25d578bb36131" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"a4653aa9db1418b0b42c4c42" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4bc7209f45e938dbe9a06f53" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e1d1343ec914619c569ce471" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69bc751e081ba4c8413c0d35" +], +[ +"relationship_scan", +"2d43e0d3c7f4c6caaaa918c3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"8c0f9f15d1e2f623e9759fd2" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"58f3cfdc7ad9b9f596472330" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"a79adccd4afe1fecf50a966f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"0ea9d405ffd41f31e9a1692d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"de0ae912351f1f8fea343321" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"08ab0d6cd271c9ff459a997a" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"df22a74dae6ef996dff2874b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e3205ce3009d03e1dd3a9599" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"543d5570b0cda50002ca64bb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6dc3ebeece372d0bc9139ac1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0ca76698a3a51c0e0d0d0426" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4f6de364740ae3b332ff48cb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"783c75eb5640db2d5f2b290f" +], +[ +"relationship_scan", +"af1783d1aa394d8dc8efac46" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"5e5289dfdac0521ab6db3aec" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c256bf13c7f77220367ed629" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a00a6ea595d3733ae6bdecbe" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dff1a4bd99dcd89fb96e7471" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"51527d8b491717ed466b0d43" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d0c61822c7e15f75af772474" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"a4147dd89bb60bf698a8a3c1" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0ec2081efe68a33434ad7a1e" +], +[ +"relationship_scan", +"5cabb4f8e322e91025151b4d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f9178d68ee95a5714fae4914" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"830f877a7d54c5cdba7c8b2f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"df365c3cfbb96c32816aaf77" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"4cefa2f3c0730b3cb42b8aef" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73859dae82bee9b7dfa986bd" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ef9e51bcdb987473521c94b9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"6f593e6dcbb047ef5565db50" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ed176f8d666276a5c6c60ffb" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9bef9351cf392c35fd43da6e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"52bd814df2a5a9a64788baab" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"d846a400d539e830953b3f82" +], +[ +"relationship_scan", +"64598a60cf2fbab0e3e8f516" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"08ab0d6cd271c9ff459a997a" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1f73e33089aaefd0bc70dbf9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"606308e2aa33431d356159c9" +], +[ +"relationship_scan", +"7153501910989191773c2c43" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ce1e0e4a2b6ca04edfa58d52" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f480b40ea3d74cda1b658593" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"42b8b3003860b0f0d61c19a6" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"505dad1e8e5daeb805d46095" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"536d660abb33441e8e64a8de" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1680474ecfff72ab3b69096c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fff031d3d2fb57915e8bf0ae" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dade70c8b81f99f201b9068c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c84551422825b00d07db0592" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6f53dbfbb6c12cd1475ed1f6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"6f53dbfbb6c12cd1475ed1f6" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"bb031880975a3b9156bce982" +], +[ +"relationship_scan", +"288e394b18c5df920f74c7f5" +], +[ +"relationship_scan", +"54c589e2944ccbc645adbc86" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"606b918a65b03052f2f21ab7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"549aa256efc9f908b87da677" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4f6de364740ae3b332ff48cb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7f242f3ffb1c5e1da26bbe7e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"897ab311321534391158d3b0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"c7963ccd56cf0b7e5f4f246f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4808bcf68fdc429cddb10fa4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1d3e74fab3790fae55c416b1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"38de4f50e1c3272b43d3b059" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"288e394b18c5df920f74c7f5" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b32930a0267a47ca0fcf72e0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"b415c68a0a1b97c7dea5db67" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"de0dbe3d87a63aed00766035" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e842e2e3b78e944c129d107e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"880c396b85f8d86911a82630" +], +[ +"relationship_scan", +"614f6c9f560555ab0c234bc0" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"82ce88ed999caa1eae0d785f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"1c6c2f444102d71b283e314d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"052573eaa0390a6201a217ae" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"04e523c88f76e02c31cbded8" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"6c7fc6500a61e035867e41ef" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"47af4bdcf2a297d8e823591c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"966921a8b19f4c77f561b736" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f80e1001d0ae21ad419b95aa" +], +[ +"relationship_scan", +"11f31172b8d083f5dbe63cfe" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"56c16bd68c6dc1ff611ea08d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"549aa256efc9f908b87da677" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"73a59804fb25f0ecae4f2c72" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4f6de364740ae3b332ff48cb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e1d1343ec914619c569ce471" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"76d0a7e7207a20a7b59b9a17" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"eccb676ecff82b2bfca75c3d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"a230fa415db6587626387775" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"3ee047a3fc52f1bf759b7fd2" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1c86943c728dd0e561dd3c39" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"797f5cd74865d4607e0b8048" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ef9e51bcdb987473521c94b9" +], +[ +"relationship_scan", +"3fed1d8c3459cb44ba949b28" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3e9a03554f568256aaa5d3aa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"b464c18ae7256268e8ec2f11" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"629f65959f74972fd7ad7778" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"55389af9cfecc685ef308d22" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d3dbe8880616f489884e8866" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae6eef41cfc22cbbb10542e9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7153501910989191773c2c43" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"86e69d42c808254305a204d2" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"893ce6832bc154cb5183f62b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"6e1ab31b695a7c38732c4a65" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3cbedd37a1030927767aa6b5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"d3dbe8880616f489884e8866" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"af0445d34e4ff8bad6074673" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"910b57542880dacde669355e" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ba699be7b74774fea43c8058" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7cfb2c910ada4d047a7f62f7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a79adccd4afe1fecf50a966f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e54dbe5595d90c5d037291cc" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c7cc1e5a5e200c04725ff145" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"ed359145aef9a571027b602a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3ba44dc5f271046bfede6ae5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"38552ebbed4cb596e0df160d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"af1783d1aa394d8dc8efac46" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3ee047a3fc52f1bf759b7fd2" +], +[ +"relationship_scan", +"3a2404c3ab2643b07d458ec5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"df7693f1f1d37a6d9b92bf58" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ac1d68abdc2de55d48de177b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8b2bae733049d89d0ac81f8d" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"287e5c659b5091e149572c2e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"761cdc5795167b2974d82e0a" +], +[ +"relationship_scan", +"d5de02525a64fd43f3bcb975" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a0e430bbbb4a1d617cd4846" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"6aff29234ed3941f837bd085" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"2f0bda8681f57fb16dec5064" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e0ce9b63b4dfa81cf1d56a26" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9bcec4aa33189fcd5df13f3c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"906fa0da3d0b30c475146a81" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe5d1c1d9aebdf87855c0d23" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"58bf151da3f8b86619e2f0c6" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"379733af007d8fcc170829ba" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e54dbe5595d90c5d037291cc" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1194ba125c6d193f69354e30" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ed0d3961b0d9591a9a995327" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"ac1d68abdc2de55d48de177b" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"aa9545fe77413bbffbc7b330" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ccca0a0f5edee67e34ce5bbf" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e842e2e3b78e944c129d107e" +], +[ +"relationship_scan", +"af2af28fb49fe10a135f75ce" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"42810d8775a639e0dba1719b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5bfe6db1bad784ca11c8c984" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7bb9e13b769a50cc6b24f7da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"500f77463140cf75f0f175c3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"041d0b2907f81598f7fb0981" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17d67719454e05f3ef4e9426" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"265149cba0f6684083cf893d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d3dbe8880616f489884e8866" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a326890560b53a2db1873674" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"0d757ab7d752d05d7bcf6ede" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a4653aa9db1418b0b42c4c42" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"79460b6734cbe299de16f443" +], +[ +"relationship_scan", +"e54dbe5595d90c5d037291cc" +], +[ +"relationship_scan", +"e842e2e3b78e944c129d107e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7e72d6cdac7555095154d1ef" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6fe7903e6f9c7569d0ebef5e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"0bf222b5d31c4701f203fd94" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"48a7f368217e6f90f0a5c729" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"29dee6c6cfd31e3ee369b9d8" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f63da3ccd846e9db1a7f3007" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7dfb2bb6ad1517422cf4e853" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"1051135bd47bdc9b22964827" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"cb774b4dd7c6429c3c521d9c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"27c59d3365ae9086cdf151ab" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"832f6e23b1252077129a8394" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7b79640ade4bb8b1f7f3c19c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a4653aa9db1418b0b42c4c42" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +] +] diff --git a/tests/goldens/python_oracle/relationship_scan_escape_prefix_dialect.json b/tests/goldens/python_oracle/relationship_scan_escape_prefix_dialect.json new file mode 100644 index 0000000000..00ca39beb6 --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_escape_prefix_dialect.json @@ -0,0 +1,12002 @@ +[ +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ce261e2e6f23671f6aed2fc" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1cd29f2eb8b48a2f903f2e97" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"606308e2aa33431d356159c9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6f53dbfbb6c12cd1475ed1f6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f14acd6c8b16faa02717acd8" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"2a4a7e955a8f7c0b4ff654e2" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"2d6175f0bf2d9b24780f45e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"65d0ed6f5d7e6e1fa23102f1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f819672b210717ae9b523d98" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d92b143d665d91db8416c45c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"60d4b932330a20cd124fefbe" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"2ecec7aced14188b31208db9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"770ea29fc3404ccbab3ee05c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"0d25f34557b281b296f591ef" +], +[ +"relationship_scan", +"b66006fb8ef92c561b80af50" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"363d6f8d3c3046dd2a97a90b" +], +[ +"relationship_scan", +"34cf1ade0acbbdd0ea219dc2" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ba5a2bfdc95eb5d95c5846a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0e385439bc0f8b93815c9e16" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3209a262594086c9a1459e2c" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"de0dbe3d87a63aed00766035" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"513a9dfe3a73e6a9d3a0ceea" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"2fe96ca5fd45b3cfa63434c7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"21881cd361793fc630acf106" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"2ac71f0f5ddec54c1d1bf172" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"2142d8612ed44bd5af78af2c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"832f6e23b1252077129a8394" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"606308e2aa33431d356159c9" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ec5dfa9a714ae4439347f68a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d9f1191ff3eb9698719d9dd6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4265a4adb9d0715c722d44c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"28e20f537dd7d89dfe99310f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"08ab0d6cd271c9ff459a997a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1e4b047df777a9b408e61184" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"08046bb3714c23b81ea23a52" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"a5fd9c8946bfbab2f1393cdf" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c25c24f82baa873429deae9" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"07b9e30b1bbf527173f0ea46" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7153501910989191773c2c43" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"eb4316081cae2bd97903b2ba" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bf1bef6516960b5981f26b31" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"2c313746f045ad664a574b92" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"549aa256efc9f908b87da677" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"af2af28fb49fe10a135f75ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"877136ece2f20d46356094fe" +], +[ +"relationship_scan", +"7e54d08512a39b4990e2e099" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3a77c1fe21582b19ebf38c7a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"a79adccd4afe1fecf50a966f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ce261e2e6f23671f6aed2fc" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9cfff16329099ef7f2c0b576" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e9f0324db7bd5811b7b965dc" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"6fc14b29b6546f40d1575186" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a79adccd4afe1fecf50a966f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"347d0d28d3079fc3524d0cd0" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"07d1a42055fd2133f9a1eac4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"718ab5b61cf444754073ad22" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a00a6ea595d3733ae6bdecbe" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d3f5e315793b71e0bc0c88f1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5a2103b44feef37480ab2ea1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d23e4166db358cde2a360a8a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"41e029dfde2d049ef33b3db6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"af2af28fb49fe10a135f75ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"78552c21ada2e56e1548e0ff" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1e92a83fb635e62241cbb96f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"cc16066bee96ae92eaba8827" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"614aac759df804db887aa5f5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"9113c81739d3662514aa74fb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ce261e2e6f23671f6aed2fc" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"4c120d9637afb9b0c1790b0b" +], +[ +"relationship_scan", +"7038314250df83bbd8a46b25" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1edfe1217ef6816ea05cda35" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"607320f8f2161c9d1ac7a0d8" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b2656fe5f2b7135a2ccdbddd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9350732a71a21c9be2e3c843" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"b405fc70ae01cd0f91c23231" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"d846a400d539e830953b3f82" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c2477534e4970a872fdb0ca7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fdf6f40489fe7e6ad9f20429" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74cede495a57877557a9bc0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c3d399dbfa6d42506702f94" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ad8fac018ec753fcd018cfa1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4f88e39c11cc2545a6cafeac" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae1c14e02f29783528c696a1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"0cf19608ce42f415ab4c4277" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"606308e2aa33431d356159c9" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"008302ab1acc71079861b7ae" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"52920ee4f817a70beec3f4aa" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7153501910989191773c2c43" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e281646bb23af2c4fc40588b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4f6de364740ae3b332ff48cb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"ff724e1df1972bc99758a258" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"cefab3931dfa14415586f93a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +] +] diff --git a/tests/goldens/python_oracle/relationship_scan_generic_dialect.json b/tests/goldens/python_oracle/relationship_scan_generic_dialect.json new file mode 100644 index 0000000000..5eda903a4e --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_generic_dialect.json @@ -0,0 +1,12002 @@ +[ +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"45cd070c5582dc2830666944" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"af9df42033a59ef1acac0c5d" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0695ad775cda6a467737a85f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c502ad4c8c29037ccd22912c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"f4c1b738d4f1d2042ed5679f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"eb6fd68a9cc723906f798e66" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"56dfc6169508717e7934e690" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"549aa256efc9f908b87da677" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"b88a7165b6052597f2c14b38" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0d384f77d21a2ffac753a2a0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"6f53dbfbb6c12cd1475ed1f6" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"79460b6734cbe299de16f443" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"0e1b72e7af07618fe11d5055" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"b04eeb496808dd48c0995cc1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"ec82330e6542e639287beefa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"01155d7e523b69b1f5520d00" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6f53dbfbb6c12cd1475ed1f6" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ef9e51bcdb987473521c94b9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"42dc5b5aeb1c895417c18db2" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f180a47eeb9c4933f8bee33f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3d909c2159a4a3b9a4c539fc" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"b7d4cfb0bc6a45e263680da2" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"2c829b9d8cb3cd47408a0bed" +], +[ +"relationship_scan", +"e308f6216b2dd3ba3828cc32" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a6f8c1a212c77be5ac3759e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5d98b8e0e275880a9236dd2e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f408b50c0152ec835ccd64bf" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3d48a90a6497543a3f053ae8" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"56f60cfcd71aac7e53b65b2d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"421149d6558d5646f2d93b9e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"81769b5710db4312df85e609" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c23fa4b659ff804e3569ec1b" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e76cf9f966c509e56b89b14d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"0cc07a9671517c44dc77d2ae" +], +[ +"relationship_scan", +"94360c70a849843e2105681f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"cfbef8434b87e0a38d8a6dce" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3114a6e9b2872c958344da6e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e9e1fc62de3c6c54842a79de" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f7562ce4c8a86cc6289690eb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a937980669efb123cb98aa5a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ac6e10b0a10fe85785865011" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"af2af28fb49fe10a135f75ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"ecc094c9096205362b06f0d0" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"81461167c67d7f609e67751b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f618cc4170841e083fc9e811" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"19296bb3b2ec52e6ed45792b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"be4802cb587fb93df0677e63" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a00a6ea595d3733ae6bdecbe" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"098ce8c721eec02b0e5497d4" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"b01ff70c7c8024ca83424077" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fb0039315b28e93c8d46e1a5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a47bd6ad794289ee181deff" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a57c70aadf57b9d735b0e4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"861af9d427d640309f55157e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ef968a9f52aee92bb99846ee" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"4c25ae7899236b090835c6db" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"de77dfae78a7ab4950c16075" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c901777d081de9e0e1000ff4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b21427bca0b4b8ab3853dfce" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5693addc29c47d33bebf4dcb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"47e0b66f32a4e57dcd3d3b1f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +] +] diff --git a/tests/goldens/python_oracle/relationship_scan_nested_comments_dialect.json b/tests/goldens/python_oracle/relationship_scan_nested_comments_dialect.json new file mode 100644 index 0000000000..665a7d0854 --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_nested_comments_dialect.json @@ -0,0 +1,12002 @@ +[ +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ed589f77ea8fa21c00ac776e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5ce261e2e6f23671f6aed2fc" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"28676bc57d6cb5224c2eaabb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"84f221397a9eab21b448d893" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"691ed5633cde0a0b8352bfbc" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d81b7bf0b3424a4ee174a60e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5d98b8e0e275880a9236dd2e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"0ef81c9272b362eae36af07e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"b8d200bc9a5e36062e460c3f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"2a1ba2f1651971d6a1558949" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"93413d4e0eb0e35d2cd5aa1c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0f1137297486fd3082ab549e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bf1bef6516960b5981f26b31" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"951de7c6a9695b79eab5d68c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f2a94373ac6b847e63a99c04" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"68ab9a875e02dd7306004d3b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f6f5bcbd7893eb406b8e86be" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7986290462d8b3e2587f7244" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"5e1d6804fe374eae91bf3159" +], +[ +"relationship_scan", +"3a440536928439156fd530cf" +], +[ +"relationship_scan", +"af2af28fb49fe10a135f75ce" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5d98b8e0e275880a9236dd2e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"cad211a8dc36cff5f814a54b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"363d6f8d3c3046dd2a97a90b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d846a400d539e830953b3f82" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"18e4ff53db33185111821443" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"97ea34a4d7b0e6b1f9bb6754" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8508a1e58836e346dee9eeaa" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"832f6e23b1252077129a8394" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5ce261e2e6f23671f6aed2fc" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"ec240be4f240d6b1f685853f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"8ba5920d97158646543d68d1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4d00fbe5bd076e73073a360f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"47946632b9728216cd56b1c4" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f9cdf97aade5823a9831739c" +], +[ +"relationship_scan", +"3ed5b887bd66bc315abbb0d0" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ff5c10defd431b61508f48a6" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a00a6ea595d3733ae6bdecbe" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"4b024d0e2335530406204f51" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a00a6ea595d3733ae6bdecbe" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"ce0ee85a93b4296093c1b658" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"832f6e23b1252077129a8394" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d8886cd0f2ebb5fbd225317" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"5cb1a4e7332b765787ed7d56" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"de0dbe3d87a63aed00766035" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f1a66383215430f9f3c833f0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4201f20ecc920461d474506a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"6c1381fd912ee8d61871a3a3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ce5d972d49c637af98e2b47d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ef9e51bcdb987473521c94b9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d94da99480df0afb3a746c14" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"870a919b6614442893edc536" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"483455993db3bce81f93e289" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"635db574c6126ed1bbac759c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bf1bef6516960b5981f26b31" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d8725a415319d88c92b56397" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"cf24c2f836084b913bd870ef" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"06ea59a7166830e1d0ec1f76" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"519ee66b98dc0c39f410f60b" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"36319bcf0996ec5108def30c" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"241f6446dab2ddad96402fb9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"39e1cb29ea8de6ecbfebcebf" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"549aa256efc9f908b87da677" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5018e2fc27a349e92ad2c514" +], +[ +"relationship_scan", +"8d407fa8d21b870aaa8c11d9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e5aae3dfd3ab6cdd92858cf3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"b720226034f24d3bc59cf3aa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7a4dc202a4e5e510088b04c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5b3198d25393608c9e54a38d" +], +[ +"relationship_scan", +"4ac473d5a38f415f6efab2d7" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"a79adccd4afe1fecf50a966f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1b58998dd9ab153fb131988a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"64a80c704fddd1138dcede4b" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"083ab7ee92be305eb936d0d0" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e91139fd35b64d91435ccb66" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"add18a1345f8afba2e0198b9" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"213d712724f65014f14e9b98" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bfae9b565e630124c07affb9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8fcf5aaa10ea643c56849e1e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"2adfffa71538a75f932e2fa0" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e89c783b729ed1764e7eb837" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +] +] diff --git a/tests/goldens/python_oracle/relationship_scan_slash_comments_dialect.json b/tests/goldens/python_oracle/relationship_scan_slash_comments_dialect.json new file mode 100644 index 0000000000..268cdbb4a6 --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_slash_comments_dialect.json @@ -0,0 +1,12002 @@ +[ +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"eb2afa6d328dd92be5f304b9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0046853319a96d03316380b4" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"bf1bef6516960b5981f26b31" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"be27c3024b27aae2f3e383c6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"17e8c4a71f6c0e623d85825c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"bd16e6564139df8319d22d95" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7aad6dc172e5bd929243b60c" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"055fb1791a2117fd1fb88dc9" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"f1a66383215430f9f3c833f0" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e56ade38b019d4f7a68977c2" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"fd86e4596dfe3a833747873e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"0c0e0d8d4427f898a9ef1bdc" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e30de3d99c2bb744eb273d12" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"eabf84bd5052c1bf02e57810" +], +[ +"relationship_scan", +"62c8dc4dab68343e4c90a583" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"de9ade866d9892820e6278e4" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ef9e51bcdb987473521c94b9" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"da455579fb071fbc72f85683" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"96c44f9c12663f2cd628ad92" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d14402daba20662061977f41" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9ce1efff6889c67a9585050b" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a0557c6134a4443adaa1be7b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"281362ff2f4c94700c9a9fb6" +], +[ +"relationship_scan", +"415c76bd8fd09531638663fc" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"992b5778bb655a00da7d71e4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d6f82e5d94843b36581be7bf" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"6e4bb09886287b3c8aad775e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"daa42f0a07a717ffad57e3d6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"4f88513270dce52b601d2f8d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8c610fdd88860558bb28e698" +], +[ +"relationship_scan", +"a26dd4604ca1463be85f240b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"92392e036493fb8448722543" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"6cf970e486b1151ea07fc787" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"5432b628257450440b1253b7" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"6ab5406cc7aebb255508e0b7" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"014780726007cb076fd3abac" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5d98b8e0e275880a9236dd2e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bf1bef6516960b5981f26b31" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ea8d47f00d8db1b50237988a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1ef683520236c27f46b94382" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"af2642902d40879fb19181d1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"69c9fad5bd6096008b06dd1d" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d846a400d539e830953b3f82" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f9a2b5a6900bbb02c1888a76" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"4c4f5ae247068467caf252fa" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"096a42122bfb3b6bc01acfaa" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1d02a84d63fafde4d1bf3c06" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9936bb7c38f56c7a1fe4e32d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fe30dd97c721722f8743b30e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ee8b43cb49f5fbf3e418a01" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"b1821a7be091282be22f17c3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"ae91faaf159c10db1dcbbbcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"33922b397222c101fb9ee721" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c577cd193800695d1504fd71" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"2f7e9201eb5edc668c12967a" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"73c0fe393e5b5690d3fca531" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"27c59d3365ae9086cdf151ab" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f94586774bff2f36b9438389" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f5c6d72e6f10acc72866da36" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"82e2a4654e00ff23660fb891" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"e2deae92e09652b5dddd22ce" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"f7f0ff1cf2ee32bd97a12f06" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"cab7bc045f7100af1343c98d" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"3cd7e1c2532296723b94064f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"f052dd673363259cf025ba48" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"8cd9a73d282b2a6324bb9b2d" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"7982bfc31674d7e1e8b8ce0a" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"e7de380006ea3938f0a5e3a4" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"11d5220b939f393a21b19853" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ae616509f741ea20a39b6210" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"b6940b3438bb9ec17ae2f3d9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"6b67da3a7a1f59ddf6e67779" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"9488a1d77871488acbebd59f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"9e3f94fe8fa71cab098c2be0" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"d4bbd58d7ad9570e86406c6c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"459e21f584cde6390509a840" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"5d98b8e0e275880a9236dd2e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"64cf60943772eda8c4ab4076" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"5ad9dd6976c2a82ea2916815" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"675d3dfdec9bb05af7d9220a" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"33026aa7b75cdb1184ce1ace" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"8c440652c8c6caf4950fc95d" +], +[ +"relationship_scan", +"8bfa6cf7d9f39cd5efb8f5fb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"400d95ea3c437722f01b4b34" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ca27a2b38f4b19dd3d4322d3" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"42f2d7be88aa1df60e95fe9e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"fe4cb0a45939795e3facf67c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"65356ad7a7360fa11cc6a4e9" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"233fb28c266f95c42fb32a46" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"555298220ce6380b9c52ef77" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"c516f3539ca9c5c120d82566" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"fb498f81d96450b36c3b1229" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"c2b0726293b9fb1d1db86c59" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"ff229f4e9827b2241dafbc10" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"f1340cb3c30a32cc60af522e" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"a3b8d8894423895f21677109" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23ba7bbbeafa7e14cd5d168b" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"97e81cd8cd956d6e9b65309f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"10ec4ab1bda9423660df12bb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"231f497d98edbebd3ddc13f5" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"5ee866e3ecad7a6c82ae9e19" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"83c936d9211631eb5da3224c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7d501e0244f982810a12eded" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"559a746f5e3da3ab0dda8d0f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"efb21d196591eb41f295ae52" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"580885eecbd90bb1f82597e5" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"4f3e015b9f88f3ebd0569cf8" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e94c126c8092ea5668a526be" +], +[ +"relationship_scan", +"26f2fafd8e8686fba727e697" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d1d91eeb5e485391898fe52b" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"de78bc9086c39bc8c6aadba5" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"00769da5cbe0dd726dfeacec" +], +[ +"relationship_scan", +"c0d6997a660484365c6f5537" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d846a400d539e830953b3f82" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"74a3816101acdc4912ac700e" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c55aaf5c93117c92e7a561fb" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1ebfd73c2d8073d2e95ad475" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"606308e2aa33431d356159c9" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"dc44f67c5bc66fc79bcb9fdd" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"90d92cd50766c7e66fbb46f8" +], +[ +"relationship_scan", +"26ec172c59ec2e1c73aeb2f3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"a5164e335ba70fc57dce10f6" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"13109da9c5f39f4f81898de1" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"724ba6d2f238cbc21a387b05" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"69e837eeb62e494a2e6d95da" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"bdf8273dcb44c1f3504a7280" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"23990d5e82c5bde1e18c01da" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +] +] diff --git a/tests/goldens/python_oracle/relationship_scan_unicode_identifier_and_keyword_edges.json b/tests/goldens/python_oracle/relationship_scan_unicode_identifier_and_keyword_edges.json new file mode 100644 index 0000000000..486bd275c4 --- /dev/null +++ b/tests/goldens/python_oracle/relationship_scan_unicode_identifier_and_keyword_edges.json @@ -0,0 +1,706 @@ +[ +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e0792de1aeaa20fa494fd6d4" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"2dcc4acb0e2e9f2bac25652b" +], +[ +"relationship_scan", +"49aef06c4b607fa7174a63d0" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"cc5e20b2e6bc8670b6e2568d" +], +[ +"relationship_scan", +"56f4bb5040f04e84faf0a311" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"14fa8747e0122561d98d0058" +], +[ +"relationship_scan", +"e6b985b0ee5fb197e2ed2949" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"ffd27c85b69ef765a7b016dd" +], +[ +"relationship_scan", +"285da5f34353c56078c50b83" +], +[ +"relationship_scan", +"96a8c0392855603d1014c040" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"be1a7ed9401aa8d1fad108e3" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e0792de1aeaa20fa494fd6d4" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"59687e5abb14c38215161fcb" +], +[ +"relationship_scan", +"05a31371bd85901f5b54fdbc" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"e49d183ef2d425fc32041cd2" +], +[ +"relationship_scan", +"2b7fe845e7053734553719b5" +], +[ +"relationship_scan", +"b384f004dd3afd2d783796eb" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"39b7acc03bee6e653a99edcb" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"56b5ffe7966f617a06ee1b55" +], +[ +"relationship_scan", +"093c1f28bc4bfb4fd6dbc7e6" +], +[ +"relationship_scan", +"c1e2f3f94e5502280de31aba" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"8d61a757a517a358c8d7a903" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"e0792de1aeaa20fa494fd6d4" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"c78d8b79df7373ab9aaa7778" +], +[ +"relationship_scan", +"56499424a43e65f1484bdd8d" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"faaffd2b633c4471c2987321" +], +[ +"relationship_scan", +"10cf149069ec1a5f92ee841b" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"1737aef2fafd640cf710093f" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"4a31d86722a6780753dfd4cf" +], +[ +"relationship_scan", +"19d94ad7d5a7c52ec98a9bb7" +], +[ +"relationship_scan", +"757d65c8bfbeff313bde8fe0" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"d8dbfd1ba87ee2b489f9b713" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"7175b348030ff7b753fe05c4" +], +[ +"relationship_scan", +"1d6a433b70aa9e8a5578b9c6" +], +[ +"relationship_scan", +"74143900a16e0949d7e4afb4" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"51bafb10380b32e748257671" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"c67071f58305b795be4165db" +], +[ +"relationship_scan", +"0496cf5ebe15f05a5b6ccfbf" +], +[ +"relationship_scan", +"670a78a12b68cebe739c1211" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"9c91343f2279b48915d0c132" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"d3e3aa061c19fa680424f623" +], +[ +"relationship_scan", +"299cee81b7e8b12105fa54e2" +], +[ +"relationship_scan", +"5b7950deac36ce4181287be5" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +], +[ +"relationship_scan", +"717b3f8e139f594f9e766616" +], +[ +"relationship_scan", +"0f122872352ce95b09ae311c" +], +[ +"relationship_scan", +"115fb5923729db29a887b522" +], +[ +"relationship_scan", +"199553205d07e4b204fe9c37" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/scopes/_test_types.py b/tests/integration/src/sqlbuild/compiler/scopes/_test_types.py index 7941204fd0..9dea85abbd 100644 --- a/tests/integration/src/sqlbuild/compiler/scopes/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/scopes/_test_types.py @@ -46,15 +46,24 @@ class ScopeEngineTierTestCase: @dataclass(frozen=True) class ExpectedNameScanTestCase: - """Seeded SQL test bodies scanned for expected models natively and in Python.""" + """Seeded SQL test bodies whose relationship scans match Python's recorded golden.""" description: str syntax: str seed: int count: int expected_minimum_scanned: int - expected_minimum_python_errors: int - expected_minimum_native_errors: int + expected_minimum_errors: int + + +@dataclass(frozen=True) +class RelationshipEdgeTestCase: + """SQL texts around Unicode identifier and keyword edges, against Python's golden.""" + + description: str + characters: tuple[str, ...] + expected_minimum_scanned: int + expected_minimum_errors: int @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/scopes/helpers.py b/tests/integration/src/sqlbuild/compiler/scopes/helpers.py index 3d7d745207..1759570dc4 100644 --- a/tests/integration/src/sqlbuild/compiler/scopes/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/scopes/helpers.py @@ -17,23 +17,14 @@ from sqlbuild.compiler.compile._helpers.attachment.declaration_scope import build_declaration_scope from sqlbuild.compiler.compile._helpers.render.declarations import resolve_declaration_context from sqlbuild.compiler.compile._helpers.render.macros import load_project_macros -from sqlbuild.compiler.compile._helpers.scenarios.core import ( - extract_sql_scenario_expected_model_names, -) -from sqlbuild.compiler.compile._helpers.sql_tests.core import ( - extract_sql_test_expected_model_names, - extract_top_level_ctes_with_scanner, -) from sqlbuild.compiler.compile._helpers.sql_tests.native import native_sql_test_ctes from sqlbuild.compiler.compile.exceptions import CompileInputError from sqlbuild.compiler.compile.models import ( - CompileSqlTestCte, DeclarationResolutionContext, DeclarationScopeBuild, DeclarationScopeResolver, LoadedMacro, ) -from sqlbuild.compiler.compile.types import SqlTestMode from sqlbuild.compiler.discovery.exceptions import DiscoveryError from sqlbuild.compiler.discovery.main.discover import discover_project_inputs from sqlbuild.compiler.discovery.models import ( @@ -73,7 +64,7 @@ VisibilityReason, ) from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax -from tests.integration.src.sqlbuild.compiler.helpers import mismatches +from tests.integration.src.sqlbuild.compiler.helpers import GoldenEntry, golden_entry PROJECT_CONFIG: str = ( 'name = "scope_parity"\nadapter = "duckdb"\n\n[connection]\ndatabase = "scope.duckdb"\n' @@ -492,13 +483,12 @@ def _lookup_shape(lookup: ScopeLookup) -> tuple[object, ...]: @dataclass(frozen=True) -class ExpectedNameScanParity: - """How the native relationship scans compared with Python over one corpus.""" +class RelationshipScans: + """The native relationship scans of one corpus as golden entries, and how many failed.""" - mismatches: list[tuple[object, object, object]] + golden: list[GoldenEntry] scanned: int - python_errors: int - native_errors: int + errors: int def generated_expected_model_sqls(*, rng: random.Random, count: int) -> list[str]: @@ -515,62 +505,69 @@ def _generated_expected_model_sql(*, rng: random.Random) -> str: return f"{rng.choice(_LEADING)}{rng.choice(_WITH)}{','.join(ctes)}{rng.choice(_TRAILING)}" -def expected_name_scan_parity( - *, sqls: list[str], syntax: SqlLexicalSyntax -) -> ExpectedNameScanParity: - """Compare each native scan, test and scenario names and test CTEs, with Python's result.""" +def relationship_scans(*, sqls: list[str], syntax: SqlLexicalSyntax) -> RelationshipScans: + """Scan each SQL text's test names, scenario names and test CTEs natively.""" test_texts: list[tuple[str, str]] = [(sql, _TEST_LABEL) for sql in sqls] scenario_texts: list[tuple[str, str]] = [(sql, _SCENARIO_LABEL) for sql in sqls] - native: list[object] = [ - *native_expected_model_names(texts=test_texts, scenario=False, syntax=syntax), - *native_expected_model_names(texts=scenario_texts, scenario=True, syntax=syntax), - *native_sql_test_ctes(texts=test_texts, syntax=syntax), - ] - python: list[object] = [ - *(_python_expected_names(sql=sql, syntax=syntax) for sql in sqls), - *(_python_scenario_names(sql=sql, syntax=syntax) for sql in sqls), - *(_python_test_ctes(sql=sql, syntax=syntax) for sql in sqls), - ] - return ExpectedNameScanParity( - mismatches=mismatches(inputs=[*sqls, *sqls, *sqls], expected=python, actual=native), - scanned=sum(isinstance(outcome, tuple) for outcome in native), - python_errors=sum(isinstance(outcome, str) for outcome in python), - native_errors=sum(isinstance(outcome, str) for outcome in native), + outcomes: list[tuple[object, object, object]] = list( + zip( + native_expected_model_names(texts=test_texts, scenario=False, syntax=syntax), + native_expected_model_names(texts=scenario_texts, scenario=True, syntax=syntax), + native_sql_test_ctes(texts=test_texts, syntax=syntax), + strict=True, + ) + ) + flat: list[object] = list(chain.from_iterable(outcomes)) + return RelationshipScans( + golden=[golden_entry("relationship_scan", outcome) for outcome in outcomes], + scanned=sum(isinstance(outcome, tuple) for outcome in flat), + errors=sum(isinstance(outcome, str) for outcome in flat), ) -def _python_expected_names(*, sql: str, syntax: SqlLexicalSyntax) -> tuple[str, ...] | str: - try: - return extract_sql_test_expected_model_names( - sql=sql, file_label=_TEST_LABEL, syntax=syntax, mode=SqlTestMode.MODEL - ) - except CompileInputError as error: - return str(error) - +UNICODE_RELATIONSHIP_CHARACTERS: tuple[str, ...] = ( + "\u0345", + "\u216b", + "\u00b2", + "\u0663", + "\u00e9", + "\u1885", + "\u2160", + "\u00aa", + "\u02b0", + "\u0300", + "\U0001d7ce", + "\u2155", + "\u00df", + "\ufb00", + "\u0130", + "\u1e9e", +) +_UNICODE_RELATIONSHIP_TEMPLATES: tuple[str, ...] = ( + "WITH {c}x AS (SELECT 1) SELECT 1", + "WITH x{c} AS (SELECT 1) SELECT 1", + "WITH __expected__a{c} AS (SELECT 1)", + "WITH __expected__a AS (SELECT 1) {c}", + "{c}WITH x AS (SELECT 1)", + "WITH{c} x AS (SELECT 1)", + "WITH x AS{c}(SELECT 1)", + "WITH x AS (SELECT 1) SELECT{c} 1", + "WITH x AS (SELECT 1) ,{c}y AS (SELECT 2)", + "wit{c} x AS (SELECT 1)", + "WITH x a{c} (SELECT 1)", +) -def _python_scenario_names(*, sql: str, syntax: SqlLexicalSyntax) -> tuple[str, ...] | str: - try: - return extract_sql_scenario_expected_model_names( - sql=sql, file_label=_SCENARIO_LABEL, syntax=syntax - ) - except CompileInputError as error: - return str(error) +def unicode_relationship_sqls(characters: tuple[str, ...]) -> list[str]: + """Each template around each character: identifier, keyword and separator edges.""" -def _python_test_ctes(*, sql: str, syntax: SqlLexicalSyntax) -> tuple[tuple[str, str], ...] | str: - try: - ctes: tuple[CompileSqlTestCte, ...] = extract_top_level_ctes_with_scanner( - sql=sql, - file_label=_TEST_LABEL, - context_label="SQL test", - with_requirement="mock CTEs and one __expected__ CTE", - cte_type=CompileSqlTestCte, - syntax=syntax, + return list( + chain.from_iterable( + map(lambda template: template.format(c=character), _UNICODE_RELATIONSHIP_TEMPLATES) + for character in characters ) - except CompileInputError as error: - return str(error) - return tuple((cte.name, cte.sql_body) for cte in ctes) + ) @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/scopes/test_native_relationship_names.py b/tests/integration/src/sqlbuild/compiler/scopes/test_native_relationship_names.py index f895659c40..40eca955fc 100644 --- a/tests/integration/src/sqlbuild/compiler/scopes/test_native_relationship_names.py +++ b/tests/integration/src/sqlbuild/compiler/scopes/test_native_relationship_names.py @@ -1,4 +1,4 @@ -"""The native relationship scans return Python's names, CTEs and errors exactly.""" +"""The native relationship scans return Python's recorded names, CTEs and errors exactly.""" from __future__ import annotations @@ -6,12 +6,22 @@ import pytest -from tests.integration.src.sqlbuild.compiler.scopes._test_types import ExpectedNameScanTestCase +from tests.integration.src.sqlbuild.compiler.helpers import ( + golden_differences, + golden_name, + read_golden, +) +from tests.integration.src.sqlbuild.compiler.scopes._test_types import ( + ExpectedNameScanTestCase, + RelationshipEdgeTestCase, +) from tests.integration.src.sqlbuild.compiler.scopes.helpers import ( LEXICAL_SYNTAXES, - ExpectedNameScanParity, - expected_name_scan_parity, + UNICODE_RELATIONSHIP_CHARACTERS, + RelationshipScans, generated_expected_model_sqls, + relationship_scans, + unicode_relationship_sqls, ) @@ -24,35 +34,59 @@ seed=20261007 + offset, count=3000, expected_minimum_scanned=300, - expected_minimum_python_errors=300, - expected_minimum_native_errors=3000, + expected_minimum_errors=3000, ) for offset, syntax in enumerate(LEXICAL_SYNTAXES) ], ids=lambda case: case.description, ) -def test_given_generated_sql_when_scanning_relationships_then_native_matches_python( +def test_given_generated_sql_when_scanning_relationships_then_native_matches_python_golden( test_case: ExpectedNameScanTestCase, ) -> None: sqls: list[str] = generated_expected_model_sqls( rng=random.Random(test_case.seed), count=test_case.count ) - parity: ExpectedNameScanParity = expected_name_scan_parity( + scans: RelationshipScans = relationship_scans( sqls=sqls, syntax=LEXICAL_SYNTAXES[test_case.syntax] ) assert ( - parity.mismatches, - parity.scanned >= test_case.expected_minimum_scanned, - parity.python_errors >= test_case.expected_minimum_python_errors, - parity.native_errors >= test_case.expected_minimum_native_errors, - ) == ([], True, True, True), ( - parity.scanned, - parity.python_errors, - parity.native_errors, + golden_differences( + read_golden(golden_name("relationship_scan", test_case.description)), scans.golden + ), + scans.scanned >= test_case.expected_minimum_scanned, + scans.errors >= test_case.expected_minimum_errors, + ) == ([], True, True), (scans.scanned, scans.errors) + + +@pytest.mark.parametrize( + "test_case", + [ + RelationshipEdgeTestCase( + description="unicode identifier and keyword edges", + characters=UNICODE_RELATIONSHIP_CHARACTERS, + expected_minimum_scanned=100, + expected_minimum_errors=100, + ) + ], + ids=lambda case: case.description, +) +def test_given_unicode_edges_when_scanning_relationships_then_native_matches_python_golden( + test_case: RelationshipEdgeTestCase, +) -> None: + scans: RelationshipScans = relationship_scans( + sqls=unicode_relationship_sqls(test_case.characters), syntax=LEXICAL_SYNTAXES["generic"] ) + assert ( + golden_differences( + read_golden(golden_name("relationship_scan", test_case.description)), scans.golden + ), + scans.scanned >= test_case.expected_minimum_scanned, + scans.errors >= test_case.expected_minimum_errors, + ) == ([], True, True), (scans.scanned, scans.errors) + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From a07499803c70cb693c2cb3c46e690ac38b37647b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:34:10 +0100 Subject: [PATCH 34/36] feat(native): run compile's explicit-reference checks natively Attached-audit gate cycles (P005), hook-read validation (unknown reads and P007 cycles) and Python-SQL hard-coded relation names (P008: project-relation index with old names, qualifier matching, temporary tables, f-string placeholders, ownership exemptions, dedup and ordering) now run in sqlbuild-analysis. Python only collects facts, including the literal SQL introspected from user functions. The replaced Python routines are deleted; a golden records the pre-port Python outcomes. --- .../_helpers/audit_gates.rs | 188 ++++++++ .../src/explicit_references/_helpers/graph.rs | 47 ++ .../_helpers/hook_reads.rs | 173 +++++++ .../src/explicit_references/_helpers/mod.rs | 5 + .../_helpers/python_sql.rs | 266 +++++++++++ .../explicit_references/_helpers/relations.rs | 153 ++++++ .../src/explicit_references/constants.rs | 26 ++ .../src/explicit_references/errors.rs | 11 + .../main/check_explicit_references.rs | 37 ++ .../src/explicit_references/main/mod.rs | 1 + .../src/explicit_references/mod.rs | 11 + .../src/explicit_references/models.rs | 151 ++++++ .../src/explicit_references/tests.rs | 6 + .../src/explicit_references/tests/helpers.rs | 60 +++ .../tests/test_explicit_references.rs | 57 +++ .../explicit_references/tests/test_types.rs | 7 + .../src/explicit_references/types.rs | 4 + crates/sqlbuild-analysis/src/lib.rs | 1 + .../_helpers/analysis/explicit_references.rs | 59 +++ .../src/bindings/_helpers/analysis/mod.rs | 1 + .../src/bindings/_helpers/analysis/queries.rs | 1 + src/sqlbuild/_native.pyi | 1 + .../compile/_helpers/assembly/audit_gates.py | 67 --- .../_helpers/explicit_references/checks.py | 422 +++++++++++++++++ .../explicit_references/hook_reads.py | 69 --- .../explicit_references/python_sql.py | 351 -------------- .../compile/main/_assemble_project.py | 30 +- .../compiler/graph/_helpers/audit_gates.py | 38 -- .../compiler/graph/_helpers/hook_reads.py | 28 +- .../graph/main/_attached_audit_gate_cycles.py | 13 - .../compiler/graph/main/_hook_read_cycles.py | 13 - .../references/_helpers/project_relations.py | 111 ----- .../main/_compiled_project_relations.py | 15 - .../explicit_references_edge_projects.json | 54 +++ .../compiler/explicit_references/__init__.py | 0 .../explicit_references/_test_types.py | 13 + .../compiler/explicit_references/helpers.py | 438 ++++++++++++++++++ .../test_native_explicit_references.py | 69 +++ 38 files changed, 2272 insertions(+), 725 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/audit_gates.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/graph.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/hook_reads.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/mod.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/python_sql.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/_helpers/relations.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/constants.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/errors.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/main/check_explicit_references.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/main/mod.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/mod.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/models.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/tests.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/tests/helpers.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/tests/test_explicit_references.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/tests/test_types.rs create mode 100644 crates/sqlbuild-analysis/src/explicit_references/types.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/analysis/explicit_references.rs delete mode 100644 src/sqlbuild/compiler/compile/_helpers/assembly/audit_gates.py create mode 100644 src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/explicit_references/hook_reads.py delete mode 100644 src/sqlbuild/compiler/compile/_helpers/explicit_references/python_sql.py delete mode 100644 src/sqlbuild/compiler/graph/main/_attached_audit_gate_cycles.py delete mode 100644 src/sqlbuild/compiler/graph/main/_hook_read_cycles.py delete mode 100644 src/sqlbuild/compiler/references/main/_compiled_project_relations.py create mode 100644 tests/goldens/python_oracle/explicit_references_edge_projects.json create mode 100644 tests/integration/src/sqlbuild/compiler/explicit_references/__init__.py create mode 100644 tests/integration/src/sqlbuild/compiler/explicit_references/_test_types.py create mode 100644 tests/integration/src/sqlbuild/compiler/explicit_references/helpers.py create mode 100644 tests/integration/src/sqlbuild/compiler/explicit_references/test_native_explicit_references.py diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/audit_gates.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/audit_gates.rs new file mode 100644 index 0000000000..d2c4b0a0b0 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/audit_gates.rs @@ -0,0 +1,188 @@ +//! Python's attached-audit gate edges and the P005 cycles they close. + +use std::collections::{HashMap, HashSet}; + +use crate::explicit_references::_helpers::graph::{Upstream, lineage_upstream, reaches}; +use crate::explicit_references::constants::{ + AUDIT_GATE_CODE, MODEL_TYPE, READABLE_TYPES, SEED_TYPE, SOURCE_TYPE, TABLE_FUNCTION_TYPE, + UDF_TYPE, +}; +use crate::explicit_references::models::{AuditGate, ReferenceDiagnostic, ReferenceRequest}; +use crate::explicit_references::types::ResourceKey; + +/// One ordering edge: `gated` waits for `read`, which an audit on `target` reads. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct GateEdge { + pub(crate) gated: ResourceKey, + pub(crate) read: ResourceKey, +} + +/// An audit's target and the other resources it reads. +struct Gate<'a> { + target: &'a ResourceKey, + reads: Vec<&'a ResourceKey>, +} + +/// `attached_audit_gate_edges_impl`: edges to a fixed point, as sources gain triggering models. +pub(crate) fn gate_edges(request: &ReferenceRequest) -> Vec { + let mut triggers: HashMap> = HashMap::new(); + for resource in request + .resources + .iter() + .filter(|item| item.key.0 == MODEL_TYPE) + { + for dep in resource.deps.iter().filter(|dep| dep.0 == SOURCE_TYPE) { + triggers + .entry(dep.clone()) + .or_default() + .insert(resource.key.clone()); + } + } + let gates: Vec> = request.audits.iter().filter_map(gate).collect(); + let mut edges: Vec = Vec::new(); + let mut seen: HashSet = HashSet::new(); + let mut changed = true; + while changed { + changed = false; + for gate in &gates { + for edge in edges_of(gate, &triggers) { + if !seen.insert(edge.clone()) { + continue; + } + changed = true; + if edge.read.0 == SOURCE_TYPE && edge.gated.0 == MODEL_TYPE { + triggers + .entry(edge.read.clone()) + .or_default() + .insert(edge.gated.clone()); + } + edges.push(edge); + } + } + } + edges +} + +/// `attached_audit_gate_cycles_impl` as Python's P005 diagnostics, in its order. +pub(crate) fn gate_diagnostics( + request: &ReferenceRequest, + edges: &[GateEdge], +) -> Vec { + let mut upstream: Upstream = lineage_upstream(&request.resources); + for edge in edges { + upstream + .entry(edge.gated.clone()) + .or_default() + .push(edge.read.clone()); + } + let mut audits: Vec<&AuditGate> = request.audits.iter().collect(); + audits.sort_by(|left, right| left.name.cmp(&right.name)); + let mut cycles: Vec<(&AuditGate, &ResourceKey, &ResourceKey)> = Vec::new(); + for audit in audits { + let Some(target) = &audit.target else { + continue; + }; + for read in extra_reads(audit, target) { + let cycle = (audit, target, read); + if reaches(&upstream, read, target) + && !cycles + .iter() + .any(|(other, t, r)| other.name == audit.name && *t == target && *r == read) + { + cycles.push(cycle); + } + } + } + cycles.sort_by(|left, right| { + (&left.0.name, &left.1.1, &left.2.1).cmp(&(&right.0.name, &right.1.1, &right.2.1)) + }); + cycles + .into_iter() + .map(|(audit, target, read)| diagnostic(request, audit, target, read)) + .collect() +} + +fn gate(audit: &AuditGate) -> Option> { + let target = audit.target.as_ref()?; + let reads: Vec<&ResourceKey> = extra_reads(audit, target); + (!reads.is_empty()).then_some(Gate { target, reads }) +} + +fn extra_reads<'a>(audit: &'a AuditGate, target: &ResourceKey) -> Vec<&'a ResourceKey> { + audit + .scope_deps + .iter() + .filter(|read| *read != target && READABLE_TYPES.contains(&read.0.as_str())) + .collect() +} + +/// `_gate_edges`: a source target gates the models it triggers, sorted by name. +fn edges_of( + gate: &Gate<'_>, + triggers: &HashMap>, +) -> Vec { + let gated: Vec = if gate.target.0 == SOURCE_TYPE { + let mut keys: Vec = triggers + .get(gate.target) + .map(|keys| keys.iter().cloned().collect()) + .unwrap_or_default(); + keys.sort_by(|left, right| left.1.cmp(&right.1)); + keys + } else { + vec![gate.target.clone()] + }; + let mut edges: Vec = Vec::new(); + for read in &gate.reads { + for key in &gated { + edges.push(GateEdge { + gated: key.clone(), + read: (*read).clone(), + }); + } + } + edges +} + +fn diagnostic( + request: &ReferenceRequest, + audit: &AuditGate, + target: &ResourceKey, + read: &ResourceKey, +) -> ReferenceDiagnostic { + let path: Option = request + .audits + .iter() + .rev() + .find(|other| other.name == audit.name) + .map(|other| other.path.clone()); + ReferenceDiagnostic { + code: AUDIT_GATE_CODE, + message: format!( + "Audit '{}' on {} reads {}, which depends on '{}'; an attached audit gates its \ + target, so it cannot wait for a resource built from that target", + audit.name, + described(target), + described(read), + target.1 + ), + help: format!( + "write a singular audit under audits/singular/ that references both '{}' and '{}'; \ + it runs once both are built", + target.1, read.1 + ), + path, + owner: None, + line: None, + resource: Some(target.clone()), + } +} + +fn described(key: &ResourceKey) -> String { + let label: &str = match key.0.as_str() { + UDF_TYPE => "function", + TABLE_FUNCTION_TYPE => "table function", + MODEL_TYPE | SOURCE_TYPE | SEED_TYPE => key.0.as_str(), + other => other, + }; + format!("{label} '{}'", key.1) +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/graph.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/graph.rs new file mode 100644 index 0000000000..337a3f0ade --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/graph.rs @@ -0,0 +1,47 @@ +//! Python's lineage upstream edges and transitive closure over compiled resource keys. + +use std::collections::{HashMap, HashSet, VecDeque}; + +use crate::explicit_references::constants::SQL_TEST_TYPE; +use crate::explicit_references::models::GraphResource; +use crate::explicit_references::types::ResourceKey; + +/// Upstream keys per resource key. +pub(crate) type Upstream = HashMap>; + +/// `build_lineage_upstream_deps_impl`: each resource's dependencies without SQL tests. +pub(crate) fn lineage_upstream(resources: &[GraphResource]) -> Upstream { + let mut upstream: Upstream = HashMap::new(); + for resource in resources { + if resource.key.0 == SQL_TEST_TYPE { + continue; + } + upstream.insert( + resource.key.clone(), + resource + .deps + .iter() + .filter(|dep| dep.0 != SQL_TEST_TYPE) + .cloned() + .collect(), + ); + } + upstream +} + +/// Whether `target` is reachable from `start` over at least one upstream edge. +pub(crate) fn reaches(upstream: &Upstream, start: &ResourceKey, target: &ResourceKey) -> bool { + let mut visited: HashSet<&ResourceKey> = HashSet::new(); + let mut frontier: VecDeque<&ResourceKey> = VecDeque::from([start]); + while let Some(current) = frontier.pop_front() { + for neighbor in upstream.get(current).into_iter().flatten() { + if visited.insert(neighbor) { + if neighbor == target { + return true; + } + frontier.push_back(neighbor); + } + } + } + false +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/hook_reads.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/hook_reads.rs new file mode 100644 index 0000000000..a81fda7c77 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/hook_reads.rs @@ -0,0 +1,173 @@ +//! Python's hook-read validation: unknown reads raise, reads of a hook's own dependant are P007. + +use std::collections::HashSet; + +use crate::explicit_references::_helpers::audit_gates::GateEdge; +use crate::explicit_references::_helpers::graph::{Upstream, lineage_upstream, reaches}; +use crate::explicit_references::constants::{ + HOOK_READ_CODE, MODEL_TYPE, PYTHON_DESTINATION, SEED_TYPE, SOURCE_TYPE, SQL_DESTINATION, +}; +use crate::explicit_references::models::{PythonHook, ReferenceDiagnostic, ReferenceRequest}; +use crate::explicit_references::types::ResourceKey; + +/// How diagnostics name a hook. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum HookType { + Python, + Sql, + InlineSql, +} + +/// One ordering edge: model `gated` waits for `read`, which one of its hooks reads. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct HookEdge { + hook_name: String, + gated: ResourceKey, + read: ResourceKey, + hook_type: HookType, + path: Option, +} + +/// The `CompileInputError` message for the first unknown read, hooks taken by name. +pub(crate) fn unknown_read(request: &ReferenceRequest) -> Option { + let mut hooks: Vec<&PythonHook> = request.hooks.iter().collect(); + hooks.sort_by(|left, right| left.name.cmp(&right.name)); + for hook in hooks { + for (kind, name) in &hook.reads { + if !known(request, kind, name) { + return Some(format!( + "Python hook '{}' in '{}' reads unknown {kind} '{name}'", + hook.name, hook.path + )); + } + } + } + None +} + +/// Whether a model, source or seed of that kind and name exists. +fn known(request: &ReferenceRequest, kind: &str, name: &str) -> bool { + [MODEL_TYPE, SOURCE_TYPE, SEED_TYPE].contains(&kind) + && request + .resources + .iter() + .any(|resource| resource.key.0 == kind && resource.key.1 == name) +} + +/// `hook_read_cycles_impl` as Python's P007 diagnostics, in its order. +pub(crate) fn hook_diagnostics( + request: &ReferenceRequest, + gates: &[GateEdge], +) -> Vec { + let mut edges: Vec = hook_edges(request); + if edges.is_empty() { + return Vec::new(); + } + let mut upstream: Upstream = lineage_upstream(&request.resources); + for gate in gates { + upstream + .entry(gate.gated.clone()) + .or_default() + .push(gate.read.clone()); + } + for edge in &edges { + upstream + .entry(edge.gated.clone()) + .or_default() + .push(edge.read.clone()); + } + edges.sort_by(|left, right| { + (&left.gated.1, &left.hook_name, &left.read.1).cmp(&( + &right.gated.1, + &right.hook_name, + &right.read.1, + )) + }); + edges + .iter() + .filter(|edge| reaches(&upstream, &edge.read, &edge.gated)) + .map(diagnostic) + .collect() +} + +/// `hook_read_edges_impl`: Python then SQL hook reads per model, deduplicated, minus self-reads. +fn hook_edges(request: &ReferenceRequest) -> Vec { + let mut candidates: Vec = Vec::new(); + for model in &request.models { + let gated: ResourceKey = (MODEL_TYPE.to_owned(), model.name.clone()); + for hook_name in &model.python_hooks { + let reads: &[ResourceKey] = request + .hooks + .iter() + .rev() + .find(|hook| &hook.name == hook_name && !hook.reads.is_empty()) + .map_or(&[], |hook| hook.reads.as_slice()); + let path: Option = request + .hooks + .iter() + .rev() + .find(|hook| &hook.name == hook_name) + .map(|hook| hook.path.clone()); + candidates.extend(reads.iter().map(|read| HookEdge { + hook_name: hook_name.clone(), + gated: gated.clone(), + read: read.clone(), + hook_type: HookType::Python, + path: path.clone(), + })); + } + for hook in &model.sql_hooks { + let (hook_name, hook_type) = match &hook.name { + Some(name) => (name.clone(), HookType::Sql), + None => ( + format!("{}[{}]", hook.hook_key, hook.index), + HookType::InlineSql, + ), + }; + let path: String = hook.path.clone().unwrap_or_else(|| model.path.clone()); + candidates.extend(hook.reads.iter().map(|read| HookEdge { + hook_name: hook_name.clone(), + gated: gated.clone(), + read: read.clone(), + hook_type, + path: Some(path.clone()), + })); + } + } + let mut seen: HashSet = HashSet::new(); + candidates + .into_iter() + .filter(|edge| edge.read != edge.gated && seen.insert(edge.clone())) + .collect() +} + +fn diagnostic(edge: &HookEdge) -> ReferenceDiagnostic { + let label: String = match edge.hook_type { + HookType::Python => format!("Python hook '{}'", edge.hook_name), + HookType::Sql => format!("SQL hook '{}'", edge.hook_name), + HookType::InlineSql => format!("inline SQL hook {}", edge.hook_name), + }; + let destination: &str = if edge.hook_type == HookType::Python { + PYTHON_DESTINATION + } else { + SQL_DESTINATION + }; + let gated: &str = &edge.gated.1; + ReferenceDiagnostic { + code: HOOK_READ_CODE, + message: format!( + "{label} on {} '{gated}' reads {} '{}', which depends on '{gated}'; a hook runs as \ + part of its model, so it cannot wait for a resource built from that model", + edge.gated.0, edge.read.0, edge.read.1 + ), + help: format!( + "attach the hook to a model built after '{}', or drop the read and use \ + {destination} for '{gated}' itself", + edge.read.1 + ), + path: edge.path.clone(), + owner: None, + line: None, + resource: Some(edge.gated.clone()), + } +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/mod.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/mod.rs new file mode 100644 index 0000000000..e6c6923006 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod audit_gates; +pub(crate) mod graph; +pub(crate) mod hook_reads; +pub(crate) mod python_sql; +pub(crate) mod relations; diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/python_sql.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/python_sql.rs new file mode 100644 index 0000000000..1932b03970 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/python_sql.rs @@ -0,0 +1,266 @@ +//! Python's P008 check of project relation names hard-coded in literal SQL in Python code. + +use std::collections::HashSet; + +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; + +use crate::explicit_references::_helpers::relations::{ + ProjectRelation, matched, project_relations, +}; +use crate::explicit_references::constants::{ + HARD_CODED_RELATION_CODE, HOOK_OWNER, LOADER_OWNER, SOURCE_TYPE, +}; +use crate::explicit_references::errors::ReferenceFailure; +use crate::explicit_references::models::{ + LiteralSql, ReferenceDiagnostic, ReferenceReport, ReferenceRequest, Relation, SqlOwner, + UnmatchedRelation, +}; +use crate::explicit_references::types::ResourceKey; +use crate::relation_names::errors::RelationNamesFailure; +use crate::relation_names::main::extract_relation_names::extract_relation_names; +use crate::relation_names::models::ExtractedRelations; + +/// A diagnostic's identity: owner label, line and the matched resource. +type FoundKey = (String, usize, ResourceKey); + +/// What one relation of one literal yields. +enum Finding { + Unmatched(UnmatchedRelation), + Diagnostic(FoundKey, ReferenceDiagnostic), +} + +/// One owner's literal SQL against the project-relation index. +struct OwnerScan<'a> { + request: &'a ReferenceRequest, + index: &'a [ProjectRelation], + owner_index: usize, + owner: &'a SqlOwner, +} + +/// `python_sql_reference_diagnostics`: P008 errors sorted by path, line and owner, and unmatched. +pub(crate) fn python_sql_report( + request: &ReferenceRequest, +) -> Result { + if request.owners.is_empty() { + return Ok(ReferenceReport::default()); + } + let index: Vec = project_relations(request); + let mut findings: Vec = Vec::new(); + for (owner_index, owner) in request.owners.iter().enumerate() { + let scan = OwnerScan { + request, + index: &index, + owner_index, + owner, + }; + findings.extend(scan.findings()?); + } + let mut keys: HashSet = HashSet::new(); + let mut found: Vec<(FoundKey, ReferenceDiagnostic)> = Vec::new(); + let mut unmatched: Vec = Vec::new(); + let mut seen: HashSet = HashSet::new(); + for finding in findings { + match finding { + Finding::Diagnostic(key, diagnostic) => { + if keys.insert(key.clone()) { + found.push((key, diagnostic)); + } + } + Finding::Unmatched(relation) => { + if seen.insert(unmatched_identity(request, &relation)) { + unmatched.push(relation); + } + } + } + } + found.sort_by_cached_key(|(key, diagnostic)| { + let owner: usize = diagnostic.owner.unwrap_or_default(); + ( + request.owners[owner].path_order, + key.1, + key.0.clone(), + key.2.clone(), + ) + }); + Ok(ReferenceReport { + diagnostics: found + .into_iter() + .map(|(_, diagnostic)| diagnostic) + .collect(), + unmatched, + }) +} + +impl OwnerScan<'_> { + /// Each literal in line order, its created temporary tables hiding later unqualified names. + fn findings(&self) -> Result, ReferenceFailure> { + let mut temporary: HashSet = HashSet::new(); + let mut literals: Vec<&LiteralSql> = self.owner.literals.iter().collect(); + literals.sort_by_key(|literal| literal.line); + let mut findings: Vec = Vec::new(); + for literal in literals { + let Some(extracted) = extracted(self.request, literal)? else { + continue; + }; + temporary.extend(extracted.temporary); + for written in extracted.relations { + let relation = Relation { + name: written.name, + schema: written.schema, + database: written.database, + }; + findings.extend(self.finding(literal, &relation, &temporary)); + } + } + Ok(findings) + } + + /// `_hard_coded_relations` for one relation of one literal. + fn finding( + &self, + literal: &LiteralSql, + relation: &Relation, + temporary: &HashSet, + ) -> Option { + if joined(relation, "").contains(&self.request.placeholder) { + return None; + } + if relation.schema.is_none() + && temporary.contains(&python_casefold(active_python_text(), &relation.name)) + { + return None; + } + let Some(found) = matched(self.index, relation) else { + return Some(Finding::Unmatched(UnmatchedRelation { + owner: self.owner_index, + line: literal.line, + method: literal.method.clone(), + relation: relation.clone(), + })); + }; + if found.compatibility_for.is_none() && self.owner.own_refs.contains(&found.target) { + return None; + } + let written: String = joined(relation, "."); + Some(Finding::Diagnostic( + (self.owner.label.clone(), literal.line, found.target.clone()), + ReferenceDiagnostic { + code: HARD_CODED_RELATION_CODE, + message: self.message(literal, &written, found), + help: self.help(relation, &written, found), + path: None, + owner: Some(self.owner_index), + line: Some(literal.line), + resource: None, + }, + )) + } + + fn message(&self, literal: &LiteralSql, written: &str, found: &ProjectRelation) -> String { + match &found.compatibility_for { + Some(model) => format!( + "{} names '{written}' in SQL passed to ctx.{}(); it is the old name of \ + model:{model}, kept only as a compatibility view for consumers outside the \ + project", + self.owner.label, literal.method + ), + None => format!( + "{} names {}:{} as '{written}' in SQL passed to ctx.{}()", + self.owner.label, found.target.0, found.target.1, literal.method + ), + } + } + + fn help(&self, relation: &Relation, written: &str, found: &ProjectRelation) -> String { + let qualify: String = if relation.schema.is_none() { + format!( + "\n = help: if '{written}' is an external table that shares the name, qualify \ + it with its schema" + ) + } else { + String::new() + }; + format!( + "{}{qualify}\n = help: {}", + self.remedy(&found.target), + self.request.explicit_help + ) + } + + /// `hard_coded_relation_remedy_impl`. + fn remedy(&self, (kind, name): &ResourceKey) -> String { + let typed: String = format!("{kind}(\"{name}\")"); + if self.owner.kind == LOADER_OWNER { + if kind != SOURCE_TYPE { + return format!( + "loaders run before models and seeds and cannot read them; read \ + {kind}:{name} from a task or asset instead" + ); + } + return match self + .owner + .upstream_loaders + .iter() + .find(|(source, _)| source == name) + { + Some((_, loader)) => { + format!("use ctx.loader({loader}) instead of the relation name") + } + None => format!("use ctx.source(\"{name}\") instead of the relation name"), + }; + } + let declaration: String = if self.owner.kind == HOOK_OWNER { + format!("@hook(reads={typed})") + } else { + format!("depends_on={typed}") + }; + format!( + "declare it with {declaration} and use ctx.relation({typed}) instead of the relation \ + name" + ) + } +} + +fn extracted( + request: &ReferenceRequest, + literal: &LiteralSql, +) -> Result, ReferenceFailure> { + extract_relation_names(&literal.sql, request.dialect.as_deref()).map_err( + |failure| match failure { + RelationNamesFailure::UnknownDialect(message) => { + ReferenceFailure::UnknownDialect(message) + } + RelationNamesFailure::Internal(reason) => ReferenceFailure::Internal(reason), + }, + ) +} + +/// The relation's non-empty database, schema and name parts joined by `separator`. +fn joined(relation: &Relation, separator: &str) -> String { + let mut parts: Vec<&str> = Vec::new(); + for part in [&relation.database, &relation.schema].into_iter().flatten() { + parts.push(part); + } + parts.push(&relation.name); + parts.retain(|part| !part.is_empty()); + parts.join(separator) +} + +/// `LiteralSqlRelation` equality: owner label, kind and path, line, method and relation. +fn unmatched_identity(request: &ReferenceRequest, item: &UnmatchedRelation) -> UnmatchedRelation { + let owner: &SqlOwner = &request.owners[item.owner]; + let canonical: usize = request + .owners + .iter() + .position(|other| { + other.label == owner.label + && other.kind == owner.kind + && other.path_order == owner.path_order + }) + .unwrap_or(item.owner); + UnmatchedRelation { + owner: canonical, + ..item.clone() + } +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/_helpers/relations.rs b/crates/sqlbuild-analysis/src/explicit_references/_helpers/relations.rs new file mode 100644 index 0000000000..69747194a0 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/_helpers/relations.rs @@ -0,0 +1,153 @@ +//! Python's compiled project-relation index and its qualifier-tolerant matcher. + +use std::collections::HashSet; + +use sqlbuild_core::text::main::active_python_text::active_python_text; +use sqlbuild_core::text::main::python_casefold::python_casefold; +use sqlbuild_core::text::main::python_strip::python_strip; + +use crate::explicit_references::constants::{ + MIGRATE_FROM_QUOTES, MODEL_TYPE, SCHEMA_QUALIFIED_PARTS, SEED_TYPE, SOURCE_TYPE, +}; +use crate::explicit_references::models::{ReferenceRequest, Relation, SourceRelation}; +use crate::explicit_references::types::ResourceKey; + +/// A relation SQLBuild manages for one resource, or a model's retained old name. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct ProjectRelation { + pub(crate) target: ResourceKey, + pub(crate) relation: Relation, + /// The model an old-name compatibility view keeps this name for. + pub(crate) compatibility_for: Option, +} + +/// `compiled_project_relations_impl`: models, seeds and table sources, then retained old names. +pub(crate) fn project_relations(request: &ReferenceRequest) -> Vec { + let mut declared: Vec = request + .models + .iter() + .map(|model| relation(MODEL_TYPE, &model.name, model.destination.clone())) + .collect(); + declared.extend( + request + .seeds + .iter() + .map(|seed| relation(SEED_TYPE, &seed.name, seed.relation.clone())), + ); + declared.extend( + request + .sources + .iter() + .filter(|source| !source.expression) + .map(source_relation), + ); + let declared: Vec = deduplicated(declared); + let old_names: Vec = old_names(request, &declared); + deduplicated(declared.into_iter().chain(old_names).collect()) +} + +/// `match_project_relation_impl` without default qualifiers: the first relation that matches. +pub(crate) fn matched<'a>( + index: &'a [ProjectRelation], + relation: &Relation, +) -> Option<&'a ProjectRelation> { + let name: String = folded(&relation.name); + index.iter().find(|candidate| { + folded(&candidate.relation.name) == name + && compatible(relation.schema.as_ref(), candidate.relation.schema.as_ref()) + && compatible( + relation.database.as_ref(), + candidate.relation.database.as_ref(), + ) + }) +} + +fn folded(text: &str) -> String { + python_casefold(active_python_text(), text) +} + +/// `_parts_compatible`: either part missing, or equal casefolded. +fn compatible(left: Option<&String>, right: Option<&String>) -> bool { + match (left, right) { + (Some(left), Some(right)) => folded(left) == folded(right), + _ => true, + } +} + +/// `_declared_old_names`: `migrate_from` names not already a declared relation. +fn old_names(request: &ReferenceRequest, declared: &[ProjectRelation]) -> Vec { + let model_names: HashSet<&str> = request + .models + .iter() + .map(|model| model.name.as_str()) + .collect(); + let mut found: Vec = Vec::new(); + for model in &request.models { + let Some(raw) = &model.migrate_from else { + continue; + }; + let parts: Vec<&str> = raw + .split('.') + .map(|part| python_strip(part).trim_matches(MIGRATE_FROM_QUOTES)) + .collect(); + if parts.len() == 1 && model_names.contains(parts[0]) { + continue; + } + let count: usize = parts.len(); + let old = Relation { + name: parts[count - 1].to_owned(), + schema: if count > 1 { + Some(parts[count - 2].to_owned()) + } else { + model.destination.schema.clone() + }, + database: if count > SCHEMA_QUALIFIED_PARTS { + Some(parts[count - 3].to_owned()) + } else { + model.destination.database.clone() + }, + }; + if matched(declared, &old).is_some() { + continue; + } + found.push(ProjectRelation { + target: (MODEL_TYPE.to_owned(), model.name.clone()), + relation: old, + compatibility_for: Some(model.name.clone()), + }); + } + found +} + +/// A table source's relation: its table, or its name where the table is unset or empty. +fn source_relation(source: &SourceRelation) -> ProjectRelation { + let name: String = match &source.table { + Some(table) if !table.is_empty() => table.clone(), + _ => source.name.clone(), + }; + relation( + SOURCE_TYPE, + &source.name, + Relation { + name, + schema: source.schema.clone(), + database: source.database.clone(), + }, + ) +} + +fn relation(kind: &str, name: &str, relation: Relation) -> ProjectRelation { + ProjectRelation { + target: (kind.to_owned(), name.to_owned()), + relation, + compatibility_for: None, + } +} + +fn deduplicated(relations: Vec) -> Vec { + let mut seen: HashSet = HashSet::new(); + relations + .into_iter() + .filter(|relation| seen.insert(relation.clone())) + .collect() +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/constants.rs b/crates/sqlbuild-analysis/src/explicit_references/constants.rs new file mode 100644 index 0000000000..2517aae27c --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/constants.rs @@ -0,0 +1,26 @@ +//! Resource kinds, codes and wording Python's explicit-reference checks use. + +pub(crate) const MODEL_TYPE: &str = "model"; +pub(crate) const SOURCE_TYPE: &str = "source"; +pub(crate) const SEED_TYPE: &str = "seed"; +pub(crate) const UDF_TYPE: &str = "udf"; +pub(crate) const TABLE_FUNCTION_TYPE: &str = "table_fn"; +pub(crate) const SQL_TEST_TYPE: &str = "sql_test"; +/// Resource types an attached audit's extra reads can gate on. +pub(crate) const READABLE_TYPES: [&str; 5] = [ + MODEL_TYPE, + SEED_TYPE, + SOURCE_TYPE, + UDF_TYPE, + TABLE_FUNCTION_TYPE, +]; +pub(crate) const AUDIT_GATE_CODE: &str = "P005"; +pub(crate) const HOOK_READ_CODE: &str = "P007"; +pub(crate) const HARD_CODED_RELATION_CODE: &str = "P008"; +pub(crate) const LOADER_OWNER: &str = "loader"; +pub(crate) const HOOK_OWNER: &str = "hook"; +pub(crate) const PYTHON_DESTINATION: &str = "ctx.destination"; +pub(crate) const SQL_DESTINATION: &str = "@@CTX:destination.qualified"; +/// The quotes Python strips from each `migrate_from` part. +pub(crate) const MIGRATE_FROM_QUOTES: [char; 2] = ['"', '`']; +pub(crate) const SCHEMA_QUALIFIED_PARTS: usize = 2; diff --git a/crates/sqlbuild-analysis/src/explicit_references/errors.rs b/crates/sqlbuild-analysis/src/explicit_references/errors.rs new file mode 100644 index 0000000000..3d112118bf --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/errors.rs @@ -0,0 +1,11 @@ +//! Why the explicit-reference checks stop. + +/// The error Python's checks raise, or an internal native failure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ReferenceFailure { + /// `CompileInputError(message)`: a Python hook reads an unknown resource. + CompileInput(String), + /// The relation-name parse's `ValueError` for an unknown dialect. + UnknownDialect(String), + Internal(String), +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/main/check_explicit_references.rs b/crates/sqlbuild-analysis/src/explicit_references/main/check_explicit_references.rs new file mode 100644 index 0000000000..21f50afd64 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/main/check_explicit_references.rs @@ -0,0 +1,37 @@ +//! Compile's explicit-reference checks over one compiled project, in Python's order. + +use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; + +use crate::explicit_references::_helpers::audit_gates::{gate_diagnostics, gate_edges}; +use crate::explicit_references::_helpers::hook_reads::{hook_diagnostics, unknown_read}; +use crate::explicit_references::_helpers::python_sql::python_sql_report; +use crate::explicit_references::errors::ReferenceFailure; +use crate::explicit_references::models::{ReferenceReport, ReferenceRequest}; + +/// P005, P007 then P008 diagnostics and unmatched Python-SQL relations, or the error Python raises. +pub fn check_explicit_references( + request: &ReferenceRequest, +) -> Result { + catch_compiler_panic(|| Ok(checked(request))) + .map_err(ReferenceFailure::Internal) + .and_then(|outcome| outcome) +} + +fn checked(request: &ReferenceRequest) -> Result { + let python_sql: ReferenceReport = if request.enforce_explicit { + python_sql_report(request)? + } else { + ReferenceReport::default() + }; + let gates = gate_edges(request); + let mut diagnostics = gate_diagnostics(request, &gates); + if let Some(message) = unknown_read(request) { + return Err(ReferenceFailure::CompileInput(message)); + } + diagnostics.extend(hook_diagnostics(request, &gates)); + diagnostics.extend(python_sql.diagnostics); + Ok(ReferenceReport { + diagnostics, + unmatched: python_sql.unmatched, + }) +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/main/mod.rs b/crates/sqlbuild-analysis/src/explicit_references/main/mod.rs new file mode 100644 index 0000000000..7cb27294a1 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/main/mod.rs @@ -0,0 +1 @@ +pub mod check_explicit_references; diff --git a/crates/sqlbuild-analysis/src/explicit_references/mod.rs b/crates/sqlbuild-analysis/src/explicit_references/mod.rs new file mode 100644 index 0000000000..a24dd11bfb --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/mod.rs @@ -0,0 +1,11 @@ +//! Compile's explicit-reference checks: audit gates, hook reads and Python-SQL relation names. + +pub(crate) mod _helpers; +mod constants; +pub mod errors; +pub mod main; +pub mod models; +pub mod types; + +#[cfg(test)] +mod tests; diff --git a/crates/sqlbuild-analysis/src/explicit_references/models.rs b/crates/sqlbuild-analysis/src/explicit_references/models.rs new file mode 100644 index 0000000000..6c730de62d --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/models.rs @@ -0,0 +1,151 @@ +//! The plain project facts the explicit-reference checks read, and what they report. + +use serde::{Deserialize, Serialize}; + +use crate::explicit_references::types::ResourceKey; + +/// Everything the checks read, collected from the compiled project and user Python by Python. +#[derive(Debug, Clone, Deserialize)] +pub struct ReferenceRequest { + /// `[references] enforce_explicit`: whether Python SQL is checked at all. + pub enforce_explicit: bool, + pub dialect: Option, + /// `explicit_references_help(allowed="hard-coded relation names")`. + pub explicit_help: String, + /// The text Python substitutes for f-string values in literal SQL. + pub placeholder: String, + /// Models, sources, seeds and functions in project order, with their dependencies. + pub resources: Vec, + pub models: Vec, + pub sources: Vec, + pub seeds: Vec, + pub audits: Vec, + /// `project.hook_functions` in order. + pub hooks: Vec, + /// Python SQL owners: tasks, assets, checks, loaders, then hooks. + pub owners: Vec, +} + +/// One resource's key and dependencies. +#[derive(Debug, Clone, Deserialize)] +pub struct GraphResource { + pub key: ResourceKey, + pub deps: Vec, +} + +/// A relation name with the qualifiers written or configured. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct Relation { + pub name: String, + pub schema: Option, + pub database: Option, +} + +/// One model's destination, hooks and old name. +#[derive(Debug, Clone, Deserialize)] +pub struct ModelReferences { + pub name: String, + pub path: String, + pub destination: Relation, + /// `model_python_hook_names(model)`. + pub python_hooks: Vec, + pub sql_hooks: Vec, + /// The `migrate_from` text where it is a string and old-name views are retained. + pub migrate_from: Option, +} + +/// One SQL hook entry with declared reads: its hook key, position and identity. +#[derive(Debug, Clone, Deserialize)] +pub struct SqlHook { + pub hook_key: String, + pub index: usize, + pub name: Option, + pub path: Option, + pub reads: Vec, +} + +/// One source: its table or name and namespace, or an expression source. +#[derive(Debug, Clone, Deserialize)] +pub struct SourceRelation { + pub name: String, + pub table: Option, + pub schema: Option, + pub database: Option, + pub expression: bool, +} + +/// A seed's name and destination. +#[derive(Debug, Clone, Deserialize)] +pub struct NamedRelation { + pub name: String, + pub relation: Relation, +} + +/// One audit: its file, attached target and scope dependencies. +#[derive(Debug, Clone, Deserialize)] +pub struct AuditGate { + pub name: String, + pub path: String, + pub target: Option, + pub scope_deps: Vec, +} + +/// One Python hook function and the resources it declares it reads. +#[derive(Debug, Clone, Deserialize)] +pub struct PythonHook { + pub name: String, + pub path: String, + pub reads: Vec, +} + +/// Python code whose literal SQL is checked, and what it may name itself. +#[derive(Debug, Clone, Deserialize)] +pub struct SqlOwner { + pub label: String, + /// `HardCodedRelationOwnerKind`: `node`, `hook` or `loader`. + pub kind: String, + /// The owner's position among its project's paths in Python's `Path` order. + pub path_order: usize, + pub own_refs: Vec, + /// Source name to the upstream loader function that writes it. + pub upstream_loaders: Vec<(String, String)>, + pub literals: Vec, +} + +/// One literal SQL string passed to `ctx.query` or `ctx.execute_sql`. +#[derive(Debug, Clone, Deserialize)] +pub struct LiteralSql { + pub method: String, + pub sql: String, + pub line: usize, +} + +/// The diagnostics in Python's order and literal SQL relations that matched no project relation. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)] +pub struct ReferenceReport { + pub diagnostics: Vec, + pub unmatched: Vec, +} + +/// One compile error diagnostic. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct ReferenceDiagnostic { + pub code: &'static str, + pub message: String, + pub help: String, + /// A file path as Python passed it, or None. + pub path: Option, + /// The owner whose path and line locate a Python-SQL diagnostic. + pub owner: Option, + pub line: Option, + pub resource: Option, +} + +/// A relation literal SQL names that is not a project relation. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] +pub struct UnmatchedRelation { + pub owner: usize, + pub line: usize, + pub method: String, + pub relation: Relation, +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/tests.rs b/crates/sqlbuild-analysis/src/explicit_references/tests.rs new file mode 100644 index 0000000000..212540b8af --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/tests.rs @@ -0,0 +1,6 @@ +#[path = "tests/test_explicit_references.rs"] +mod explicit_references; +#[path = "tests/helpers.rs"] +mod helpers; +#[path = "tests/test_types.rs"] +mod test_types; diff --git a/crates/sqlbuild-analysis/src/explicit_references/tests/helpers.rs b/crates/sqlbuild-analysis/src/explicit_references/tests/helpers.rs new file mode 100644 index 0000000000..132b5bad09 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/tests/helpers.rs @@ -0,0 +1,60 @@ +use crate::explicit_references::models::{ + ModelReferences, NamedRelation, ReferenceRequest, Relation, SourceRelation, +}; + +pub(crate) fn relation(name: &str, schema: Option<&str>, database: Option<&str>) -> Relation { + Relation { + name: name.to_owned(), + schema: schema.map(str::to_owned), + database: database.map(str::to_owned), + } +} + +fn model(name: &str, migrate_from: Option<&str>) -> ModelReferences { + ModelReferences { + name: name.to_owned(), + path: format!("models/{name}.sql"), + destination: relation(name, Some("main"), Some("memory")), + python_hooks: Vec::new(), + sql_hooks: Vec::new(), + migrate_from: migrate_from.map(str::to_owned), + } +} + +pub(crate) fn request() -> ReferenceRequest { + ReferenceRequest { + enforce_explicit: true, + dialect: None, + explicit_help: String::new(), + placeholder: "__sqlbuild_python_value_".to_owned(), + resources: Vec::new(), + models: vec![ + model("customers", Some(" `legacy_customers` ")), + model("orders", Some("\"archive\".old_orders")), + model("regions", Some("customers")), + ], + sources: vec![ + SourceRelation { + name: "raw_orders".to_owned(), + table: Some(String::new()), + schema: Some("landing".to_owned()), + database: None, + expression: false, + }, + SourceRelation { + name: "computed".to_owned(), + table: None, + schema: None, + database: None, + expression: true, + }, + ], + seeds: vec![NamedRelation { + name: "country_codes".to_owned(), + relation: relation("country_codes", Some("seeds"), None), + }], + audits: Vec::new(), + hooks: Vec::new(), + owners: Vec::new(), + } +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/tests/test_explicit_references.rs b/crates/sqlbuild-analysis/src/explicit_references/tests/test_explicit_references.rs new file mode 100644 index 0000000000..9fe8f52a87 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/tests/test_explicit_references.rs @@ -0,0 +1,57 @@ +use crate::explicit_references::_helpers::relations::{matched, project_relations}; +use crate::explicit_references::tests::helpers::{relation, request}; +use crate::explicit_references::tests::test_types::MatchTestCase; + +#[test] +fn given_written_relations_when_matching_project_relations_then_matches_python() { + let test_cases = [ + MatchTestCase { + description: "an unqualified name matches case-insensitively", + written: ("CUSTOMERS", None, None), + expected_match: Some(("model", "customers", None)), + }, + MatchTestCase { + description: "a conflicting schema does not match", + written: ("customers", Some("other"), None), + expected_match: None, + }, + MatchTestCase { + description: "an empty source table falls back to the source name", + written: ("raw_orders", Some("LANDING"), None), + expected_match: Some(("source", "raw_orders", None)), + }, + MatchTestCase { + description: "an expression source is no relation", + written: ("computed", None, None), + expected_match: None, + }, + MatchTestCase { + description: "a stripped, unquoted old name inherits the destination namespace", + written: ("legacy_customers", Some("main"), Some("memory")), + expected_match: Some(("model", "customers", Some("customers"))), + }, + MatchTestCase { + description: "a schema-qualified old name keeps its schema", + written: ("old_orders", Some("archive"), None), + expected_match: Some(("model", "orders", Some("orders"))), + }, + MatchTestCase { + description: "an old name equal to another model is that model, not an old name", + written: ("customers", None, None), + expected_match: Some(("model", "customers", None)), + }, + ]; + let index = project_relations(&request()); + + for test_case in test_cases { + let (name, schema, database) = test_case.written; + let found = matched(&index, &relation(name, schema, database)).map(|found| { + ( + found.target.0.as_str(), + found.target.1.as_str(), + found.compatibility_for.as_deref(), + ) + }); + assert_eq!(found, test_case.expected_match, "{}", test_case.description); + } +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/tests/test_types.rs b/crates/sqlbuild-analysis/src/explicit_references/tests/test_types.rs new file mode 100644 index 0000000000..3d4be469e1 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/tests/test_types.rs @@ -0,0 +1,7 @@ +pub(crate) struct MatchTestCase { + pub(crate) description: &'static str, + /// `(name, schema, database)` written in SQL. + pub(crate) written: (&'static str, Option<&'static str>, Option<&'static str>), + /// The matched `(kind, name, compatibility_for)`, or None. + pub(crate) expected_match: Option<(&'static str, &'static str, Option<&'static str>)>, +} diff --git a/crates/sqlbuild-analysis/src/explicit_references/types.rs b/crates/sqlbuild-analysis/src/explicit_references/types.rs new file mode 100644 index 0000000000..7e1941e481 --- /dev/null +++ b/crates/sqlbuild-analysis/src/explicit_references/types.rs @@ -0,0 +1,4 @@ +//! Explicit-reference key aliases. + +/// A `CompiledObjectKey`: `(resource type, name)`; typed SQL refs use the same kind names. +pub type ResourceKey = (String, String); diff --git a/crates/sqlbuild-analysis/src/lib.rs b/crates/sqlbuild-analysis/src/lib.rs index 29ec9b3310..4d62706cbb 100644 --- a/crates/sqlbuild-analysis/src/lib.rs +++ b/crates/sqlbuild-analysis/src/lib.rs @@ -7,6 +7,7 @@ pub mod column_references; pub mod compiler; mod constants; pub mod contracts; +pub mod explicit_references; pub mod lineage; pub mod query_analysis; pub mod relation_names; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/explicit_references.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/explicit_references.rs new file mode 100644 index 0000000000..23790b3674 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/explicit_references.rs @@ -0,0 +1,59 @@ +//! Compile's explicit-reference checks over plain project facts, run natively. + +use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::{pyfunction, wrap_pyfunction}; +use serde_json::{Value, json}; +use sqlbuild_analysis::explicit_references::errors::ReferenceFailure; +use sqlbuild_analysis::explicit_references::main::check_explicit_references::check_explicit_references; +use sqlbuild_analysis::explicit_references::models::{ReferenceReport, ReferenceRequest}; +use sqlbuild_core::panics::main::native_failure::native_failure; + +use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::types::CompilerDetach; + +const EXPLICIT_REFERENCES_CONTEXT: &str = "native explicit-reference checks"; + +/// Python's audit-gate, hook-read and Python-SQL checks: `{report}` or `{raise}` JSON. +#[pyfunction] +fn check_native_explicit_references(py: Python<'_>, request_json: &str) -> PyResult { + let request: ReferenceRequest = serde_json::from_str(request_json).map_err(|error| { + compiler_error(native_failure( + EXPLICIT_REFERENCES_CONTEXT, + &error.to_string(), + )) + })?; + let outcome: Value = match py + .compiler_detach(|| Ok(check_explicit_references(&request))) + .map_err(compiler_error)? + { + Ok(report) => json!({ "report": report_value(&report)? }), + Err(ReferenceFailure::CompileInput(message)) => json!({ "raise": message }), + Err(ReferenceFailure::UnknownDialect(message)) => return Err(compiler_error(message)), + Err(ReferenceFailure::Internal(reason)) => { + return Err(compiler_error(native_failure( + EXPLICIT_REFERENCES_CONTEXT, + &reason, + ))); + } + }; + serde_json::to_string(&outcome).map_err(|error| { + compiler_error(native_failure( + EXPLICIT_REFERENCES_CONTEXT, + &error.to_string(), + )) + }) +} + +fn report_value(report: &ReferenceReport) -> PyResult { + serde_json::to_value(report).map_err(|error| { + compiler_error(native_failure( + EXPLICIT_REFERENCES_CONTEXT, + &error.to_string(), + )) + }) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(check_native_explicit_references, module)?)?; + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs index ab9f02c8f4..952f5ba328 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/mod.rs @@ -1,5 +1,6 @@ mod catalog; pub(crate) mod conversions; +pub(crate) mod explicit_references; pub(crate) mod normalization; pub(crate) mod normalization_results; mod positions; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs index da1fd8f727..6a7ccfa676 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/queries.rs @@ -72,6 +72,7 @@ fn analyze_project_queries_compact_json(py: Python<'_>, request_json: &str) -> P pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { crate::bindings::_helpers::analysis::relation_names::register(module)?; + crate::bindings::_helpers::analysis::explicit_references::register(module)?; module.add_function(wrap_pyfunction!(query_fingerprint, module)?)?; module.add_function(wrap_pyfunction!(schema_validation_json, module)?)?; module.add_function(wrap_pyfunction!(schema_validations_json, module)?)?; diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index df99137fce..51920c796c 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -698,6 +698,7 @@ def assemble_project_resource_facts( list[bool], tuple[str, int, str, str, str | None, int | None] | None, ]: ... +def check_native_explicit_references(request_json: str, /) -> str: ... def _oracle_json_dumps(dialect_json: str, value_json: str) -> str: ... def _oracle_text_positions( data: bytes, byte_offsets: list[int] diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/audit_gates.py b/src/sqlbuild/compiler/compile/_helpers/assembly/audit_gates.py deleted file mode 100644 index abce2fc5ab..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/audit_gates.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Compile validation for attached audits that gate their target.""" - -from __future__ import annotations - -from pathlib import Path - -from sqlbuild.compiler.compile.constants import ATTACHED_AUDIT_READS_OWN_DEPENDANT_CODE -from sqlbuild.compiler.compile.models import ( - CompiledObjectKey, - CompiledProject, - CompilerDiagnostic, -) -from sqlbuild.compiler.compile.types import ( - CompiledResourceType, - DiagnosticPhase, - DiagnosticSeverity, -) -from sqlbuild.compiler.graph.main._attached_audit_gate_cycles import attached_audit_gate_cycles -from sqlbuild.compiler.graph.models import AttachedAuditGateEdge - -_RESOURCE_LABELS: dict[CompiledResourceType, str] = { - CompiledResourceType.MODEL: "model", - CompiledResourceType.SOURCE: "source", - CompiledResourceType.SEED: "seed", - CompiledResourceType.UDF: "function", - CompiledResourceType.TABLE_FN: "table function", -} - - -def attached_audit_gate_diagnostics(*, project: CompiledProject) -> tuple[CompilerDiagnostic, ...]: - """Return a P005 error for each attached audit that reads a resource built from its target.""" - - audit_paths: dict[str, Path] = { - audit.name: audit.audit_file.relative_path for audit in project.audits - } - return tuple( - _cycle_diagnostic(cycle=cycle, audit_path=audit_paths.get(cycle.audit_name)) - for cycle in attached_audit_gate_cycles(project=project) - ) - - -def _cycle_diagnostic( - *, cycle: AttachedAuditGateEdge, audit_path: Path | None -) -> CompilerDiagnostic: - target: str = _describe(cycle.target) - read: str = _describe(cycle.read) - return CompilerDiagnostic( - phase=DiagnosticPhase.COMPILE, - severity=DiagnosticSeverity.ERROR, - code=ATTACHED_AUDIT_READS_OWN_DEPENDANT_CODE, - message=( - f"Audit '{cycle.audit_name}' on {target} reads {read}, which depends on " - f"'{cycle.target.name}'; an attached audit gates its target, so it cannot wait for a " - "resource built from that target" - ), - resource_type=cycle.target.resource_type, - resource_name=cycle.target.name, - path=audit_path, - help=( - f"write a singular audit under audits/singular/ that references both " - f"'{cycle.target.name}' and '{cycle.read.name}'; it runs once both are built" - ), - ) - - -def _describe(key: CompiledObjectKey) -> str: - return f"{_RESOURCE_LABELS[CompiledResourceType(key.resource_type)]} '{key.name}'" diff --git a/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py b/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py new file mode 100644 index 0000000000..7a0971e489 --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py @@ -0,0 +1,422 @@ +"""Compile's explicit-reference checks: Python collects the facts, native decides.""" + +from __future__ import annotations + +import ast +import inspect +import textwrap +from collections.abc import Callable, Iterator, Mapping +from dataclasses import dataclass, field +from itertools import chain +from pathlib import Path +from typing import Any + +import orjson + +import sqlbuild._native as _native +from sqlbuild.compiler.compile.constants import MIGRATE_FROM_CONFIG_KEY +from sqlbuild.compiler.compile.exceptions import CompileInputError +from sqlbuild.compiler.compile.models import ( + CompiledAudit, + CompiledModel, + CompiledObjectKey, + CompiledProject, + CompiledRelationLocation, + CompiledSource, + CompilerDiagnostic, + PythonSqlReferenceReport, +) +from sqlbuild.compiler.compile.types import ( + AttachedAuditTargetKind, + DiagnosticPhase, + DiagnosticSeverity, +) +from sqlbuild.compiler.discovery.main.explicit_references_help import explicit_references_help +from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs, SqlHookEntry +from sqlbuild.compiler.graph.main._model_python_hook_names import model_python_hook_names +from sqlbuild.compiler.migrations.main._resolve_old_name_view_retention import ( + resolve_old_name_view_retention, +) +from sqlbuild.compiler.references.models import LiteralSqlRelation, RelationName +from sqlbuild.compiler.references.types import HardCodedRelationOwnerKind +from sqlbuild.python_nodes.main.read_loader_definition import read_loader_definition +from sqlbuild.python_nodes.models import LoaderDefinition, SqlResourceRef +from sqlbuild.python_nodes.types import SqlResourceRefKind +from sqlbuild.spec.contracts.models import SourceEntry, SourceLocation + +_MODEL_HOOK_KEYS: tuple[str, ...] = ("pre_hooks", "post_hooks") +_RAISE_KEY: str = "raise" +_REPORT_KEY: str = "report" +_SQL_METHOD_NAMES: frozenset[str] = frozenset({"query", "execute_sql"}) +_SQL_PARAMETER_NAME: str = "sql" +_CONTEXT_PARAMETER_NAMES: frozenset[str] = frozenset({"ctx", "context", "_ctx", "hook_context"}) +_VALUE_PLACEHOLDER: str = "__sqlbuild_python_value_" + + +@dataclass(frozen=True) +class _PythonSqlOwner: + label: str + kind: HardCodedRelationOwnerKind + function: Callable[..., object] + relative_path: Path + own_refs: frozenset[SqlResourceRef] = frozenset() + upstream_loader_by_source: Mapping[str, str] = field(default_factory=dict) + + +@dataclass(frozen=True) +class _LiteralSql: + method: str + sql: str + line: int + + +def explicit_reference_report( + *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs, enforce_explicit: bool +) -> PythonSqlReferenceReport: + """Return P005, P007 and P008 errors and unmatched Python-SQL relations; raise P001.""" + + owners: tuple[_PythonSqlOwner, ...] = ( + _owners(project=project, discovered_inputs=discovered_inputs) if enforce_explicit else () + ) + literals: tuple[tuple[_LiteralSql, ...], ...] = tuple( + tuple(_literal_sql_calls(owner.function)) for owner in owners + ) + paths: list[Path] = sorted({owner.relative_path for owner in owners}) + path_order: dict[Path, int] = {path: index for index, path in enumerate(paths)} + outcome: dict[str, Any] = orjson.loads( + _native.check_native_explicit_references( + orjson.dumps( + { + "enforce_explicit": enforce_explicit, + "dialect": project.sql_analysis_dialect, + "explicit_help": ( + explicit_references_help(allowed="hard-coded relation names") + if enforce_explicit + else "" + ), + "placeholder": _VALUE_PLACEHOLDER, + "resources": _resource_rows(project), + "models": [ + _model_row( + model=model, + old_name_retention=( + discovered_inputs.project_config.migrations.old_name_views + ), + ) + for model in project.models + ], + "sources": [_source_row(source) for source in project.sources], + "seeds": [ + {"name": seed.name, "relation": _location_row(seed.destination)} + for seed in project.seeds + ], + "audits": [_audit_row(audit) for audit in project.audits], + "hooks": [ + { + "name": hook.name, + "path": str(hook.relative_path), + "reads": _ref_rows(hook.reads), + } + for hook in project.hook_functions + ], + "owners": [ + _owner_row( + owner=owner, + literals=owner_literals, + path_order=path_order[owner.relative_path], + ) + for owner, owner_literals in zip(owners, literals, strict=True) + ], + } + ).decode() + ) + ) + if _RAISE_KEY in outcome: + raise CompileInputError(outcome[_RAISE_KEY]) + report: dict[str, Any] = outcome[_REPORT_KEY] + return PythonSqlReferenceReport( + diagnostics=tuple(_diagnostic(row=row, owners=owners) for row in report["diagnostics"]), + unmatched=tuple( + LiteralSqlRelation( + owner_label=owners[row["owner"]].label, + owner_kind=owners[row["owner"]].kind, + relative_path=owners[row["owner"]].relative_path, + line=row["line"], + method=row["method"], + relation=RelationName( + name=row["relation"]["name"], + schema=row["relation"]["schema"], + database=row["relation"]["database"], + ), + ) + for row in report["unmatched"] + ), + ) + + +def _diagnostic(*, row: dict[str, Any], owners: tuple[_PythonSqlOwner, ...]) -> CompilerDiagnostic: + owner: int | None = row["owner"] + resource: list[str] | None = row["resource"] + return CompilerDiagnostic( + phase=DiagnosticPhase.COMPILE, + severity=DiagnosticSeverity.ERROR, + code=row["code"], + message=row["message"], + resource_type=None if resource is None else resource[0], + resource_name=None if resource is None else resource[1], + path=None if row["path"] is None else Path(row["path"]), + location=( + None + if owner is None + else SourceLocation(path=owners[owner].relative_path, line=row["line"], column=1) + ), + help=row["help"], + ) + + +def _owner_row( + *, owner: _PythonSqlOwner, literals: tuple[_LiteralSql, ...], path_order: int +) -> dict[str, object]: + return { + "label": owner.label, + "kind": str(owner.kind), + "path_order": path_order, + "own_refs": _ref_rows(tuple(owner.own_refs)), + "upstream_loaders": list(owner.upstream_loader_by_source.items()), + "literals": [ + {"method": literal.method, "sql": literal.sql, "line": literal.line} + for literal in literals + ], + } + + +def _resource_rows(project: CompiledProject) -> list[dict[str, object]]: + return [ + {"key": _key_row(resource.key), "deps": _key_rows(resource.deps)} + for resource in (*project.models, *project.sources, *project.seeds, *project.functions) + ] + + +def _key_row(key: CompiledObjectKey) -> tuple[str, str]: + return (str(key.resource_type), key.name) + + +def _key_rows(keys: tuple[CompiledObjectKey, ...]) -> list[tuple[str, str]]: + return [_key_row(key) for key in keys] + + +def _model_row(*, model: CompiledModel, old_name_retention: str | None) -> dict[str, object]: + raw: object = model.config.values.get(MIGRATE_FROM_CONFIG_KEY) + retained: bool = isinstance(raw, str) and ( + resolve_old_name_view_retention( + config_values=model.config.values, project_retention=old_name_retention + ) + is not None + ) + return { + "name": model.name, + "path": str(model.relative_path), + "destination": _location_row(model.destination), + "python_hooks": list(model_python_hook_names(model=model)), + "sql_hooks": list( + chain.from_iterable( + _sql_hook_rows(hook_key=hook_key, entries=model.config.values.get(hook_key)) + for hook_key in _MODEL_HOOK_KEYS + ) + ), + "migrate_from": raw if retained else None, + } + + +def _sql_hook_rows(*, hook_key: str, entries: object) -> list[dict[str, object]]: + """SQL hook entries with declared reads, at their position among the key's entries.""" + + items: tuple[object, ...] = tuple(entries) if isinstance(entries, list | tuple) else (entries,) + return [ + { + "hook_key": hook_key, + "index": index, + "name": entry.name, + "path": None if entry.relative_path is None else str(entry.relative_path), + "reads": _ref_rows(entry.reads), + } + for index, entry in enumerate(items) + if isinstance(entry, SqlHookEntry) and entry.reads + ] + + +def _source_row(source: CompiledSource) -> dict[str, object]: + entry: SourceEntry = source.source_entry + return { + "name": source.name, + "table": entry.table, + "schema": entry.schema, + "database": entry.database, + "expression": entry.expression is not None, + } + + +def _audit_row(audit: CompiledAudit) -> dict[str, object]: + target: tuple[str, str] | None = ( + ( + str(AttachedAuditTargetKind(audit.attached_target_kind).resource_type), + audit.attached_target_name, + ) + if audit.attached_target_kind is not None and audit.attached_target_name is not None + else None + ) + return { + "name": audit.name, + "path": str(audit.audit_file.relative_path), + "target": target, + "scope_deps": _key_rows(audit.scope_deps), + } + + +def _location_row(location: CompiledRelationLocation) -> dict[str, str | None]: + return {"name": location.name, "schema": location.schema, "database": location.database} + + +def _ref_rows(refs: tuple[SqlResourceRef, ...]) -> list[tuple[str, str]]: + return [(str(ref.kind), ref.name) for ref in refs] + + +def _owners( + *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs +) -> tuple[_PythonSqlOwner, ...]: + owners: list[_PythonSqlOwner] = [] + for label, nodes in ( + ("task", discovered_inputs.task_functions), + ("asset", discovered_inputs.asset_functions), + ("check", discovered_inputs.check_functions), + ): + owners.extend( + _PythonSqlOwner( + label=f"{label}:{node.name}", + kind=HardCodedRelationOwnerKind.NODE, + function=node.function, + relative_path=node.relative_path, + ) + for node in nodes + ) + owners.extend(_loader_owners(project=project, discovered_inputs=discovered_inputs)) + attached_models: dict[str, set[SqlResourceRef]] = {} + for model in project.models: + for hook_name in model_python_hook_names(model=model): + attached_models.setdefault(hook_name, set()).add( + SqlResourceRef(kind=SqlResourceRefKind.MODEL, name=model.name) + ) + owners.extend( + _PythonSqlOwner( + label=f"hook:{hook.name}", + kind=HardCodedRelationOwnerKind.HOOK, + function=hook.function, + relative_path=hook.relative_path, + own_refs=frozenset(attached_models.get(hook.name, ())), + ) + for hook in project.hook_functions + ) + return tuple(owners) + + +def _loader_owners( + *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs +) -> tuple[_PythonSqlOwner, ...]: + sources_by_loader: dict[str, set[str]] = {} + for source in project.sources: + if source.source_entry.loader is not None: + sources_by_loader.setdefault(source.source_entry.loader, set()).add(source.name) + owners: list[_PythonSqlOwner] = [] + for loader in discovered_inputs.loader_functions: + upstream_loader_by_source: dict[str, str] = {} + for dependency in loader.depends_on: + definition: LoaderDefinition | None = ( + read_loader_definition(dependency) if callable(dependency) else None + ) + if definition is None: + continue + for source_name in sources_by_loader.get(definition.name, ()): + upstream_loader_by_source[source_name] = getattr( + dependency, "__name__", definition.name + ) + owners.append( + _PythonSqlOwner( + label=f"loader:{loader.name}", + kind=HardCodedRelationOwnerKind.LOADER, + function=loader.function, + relative_path=loader.relative_path, + own_refs=frozenset( + SqlResourceRef(kind=SqlResourceRefKind.SOURCE, name=source_name) + for source_name in sources_by_loader.get(loader.name, ()) + ), + upstream_loader_by_source=upstream_loader_by_source, + ) + ) + return tuple(owners) + + +def _literal_sql_calls(function: Callable[..., object]) -> Iterator[_LiteralSql]: + try: + lines: list[str] + start_line: int + lines, start_line = inspect.getsourcelines(function) + tree: ast.Module = ast.parse(textwrap.dedent("".join(lines))) + except (OSError, TypeError, SyntaxError): + return + definition: ast.stmt | None = tree.body[0] if tree.body else None + if not isinstance(definition, ast.FunctionDef | ast.AsyncFunctionDef): + return + parameters: list[str] = [ + argument.arg + for argument in ( + *definition.args.posonlyargs, + *definition.args.args, + *definition.args.kwonlyargs, + ) + ] + context_name: str | None = next( + (name for name in parameters if name in _CONTEXT_PARAMETER_NAMES), + parameters[0] if parameters else None, + ) + if context_name is None: + return + for node in ast.walk(definition): + if not ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) + and node.func.attr in _SQL_METHOD_NAMES + and isinstance(node.func.value, ast.Name) + and node.func.value.id == context_name + ): + continue + argument: ast.expr | None = _sql_argument(node) + sql: str | None = _literal_text(argument) if argument is not None else None + if sql is not None: + yield _LiteralSql(method=node.func.attr, sql=sql, line=start_line + node.lineno - 1) + + +def _sql_argument(call: ast.Call) -> ast.expr | None: + if call.args: + return call.args[0] + for keyword in call.keywords: + if keyword.arg == _SQL_PARAMETER_NAME: + return keyword.value + if keyword.arg is None and isinstance(keyword.value, ast.Dict): + for key, value in zip(keyword.value.keys, keyword.value.values, strict=True): + if isinstance(key, ast.Constant) and key.value == _SQL_PARAMETER_NAME: + return value + return None + + +def _literal_text(node: ast.expr) -> str | None: + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if not isinstance(node, ast.JoinedStr): + return None + parts: list[str] = [] + for index, value in enumerate(node.values): + if isinstance(value, ast.Constant) and isinstance(value.value, str): + parts.append(value.value) + else: + parts.append(f"{_VALUE_PLACEHOLDER}{index}") + return "".join(parts) diff --git a/src/sqlbuild/compiler/compile/_helpers/explicit_references/hook_reads.py b/src/sqlbuild/compiler/compile/_helpers/explicit_references/hook_reads.py deleted file mode 100644 index 3b4ffa6384..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/explicit_references/hook_reads.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Compile validation for resources model hooks read.""" - -from __future__ import annotations - -from sqlbuild.compiler.compile.constants import HOOK_READS_OWN_DEPENDANT_CODE -from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.compile.models import ( - CompiledObjectKey, - CompiledProject, - CompilerDiagnostic, -) -from sqlbuild.compiler.compile.types import ( - CompiledResourceType, - DiagnosticPhase, - DiagnosticSeverity, -) -from sqlbuild.compiler.graph.main._hook_read_cycles import hook_read_cycles -from sqlbuild.compiler.graph.models import HookReadEdge -from sqlbuild.compiler.graph.types import HookReadType -from sqlbuild.python_nodes.models import SqlResourceRef -from sqlbuild.python_nodes.types import SqlResourceRefKind - - -def hook_read_diagnostics(*, project: CompiledProject) -> tuple[CompilerDiagnostic, ...]: - """Reject unknown hook reads; return a P007 error for each read of a hook's own dependant.""" - - known: dict[SqlResourceRefKind, frozenset[str]] = { - SqlResourceRefKind.MODEL: frozenset(model.name for model in project.models), - SqlResourceRefKind.SOURCE: frozenset(source.name for source in project.sources), - SqlResourceRefKind.SEED: frozenset(seed.name for seed in project.seeds), - } - for hook in sorted(project.hook_functions, key=lambda item: item.name): - ref: SqlResourceRef - for ref in hook.reads: - if ref.name not in known[ref.kind]: - raise CompileInputError( - f"Python hook '{hook.name}' in '{hook.relative_path.as_posix()}' reads unknown " - f"{ref.kind.value} '{ref.name}'" - ) - return tuple(_cycle_diagnostic(cycle) for cycle in hook_read_cycles(project=project)) - - -def _cycle_diagnostic(cycle: HookReadEdge) -> CompilerDiagnostic: - destination: str = ( - "ctx.destination" - if cycle.hook_type is HookReadType.PYTHON - else "@@CTX:destination.qualified" - ) - return CompilerDiagnostic( - phase=DiagnosticPhase.COMPILE, - severity=DiagnosticSeverity.ERROR, - code=HOOK_READS_OWN_DEPENDANT_CODE, - message=( - f"{cycle.label} on {_describe(cycle.gated)} reads " - f"{_describe(cycle.read)}, which depends on '{cycle.gated.name}'; a hook runs as " - "part of its model, so it cannot wait for a resource built from that model" - ), - resource_type=CompiledResourceType.MODEL, - resource_name=cycle.gated.name, - path=cycle.hook_path, - help=( - f"attach the hook to a model built after '{cycle.read.name}', or drop the read and " - f"use {destination} for '{cycle.gated.name}' itself" - ), - ) - - -def _describe(key: CompiledObjectKey) -> str: - return f"{key.resource_type} '{key.name}'" diff --git a/src/sqlbuild/compiler/compile/_helpers/explicit_references/python_sql.py b/src/sqlbuild/compiler/compile/_helpers/explicit_references/python_sql.py deleted file mode 100644 index bcafdfbbaa..0000000000 --- a/src/sqlbuild/compiler/compile/_helpers/explicit_references/python_sql.py +++ /dev/null @@ -1,351 +0,0 @@ -"""Compile-time rejection of project relation names hard-coded in Python SQL.""" - -from __future__ import annotations - -import ast -import inspect -import textwrap -from collections.abc import Callable, Iterator, Mapping -from dataclasses import dataclass, field -from pathlib import Path - -from sqlbuild.compiler.compile.constants import HARD_CODED_PROJECT_RELATION_CODE -from sqlbuild.compiler.compile.models import ( - CompiledProject, - CompilerDiagnostic, - PythonSqlReferenceReport, -) -from sqlbuild.compiler.compile.types import DiagnosticPhase, DiagnosticSeverity -from sqlbuild.compiler.discovery.main.explicit_references_help import explicit_references_help -from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs -from sqlbuild.compiler.graph.main._model_python_hook_names import model_python_hook_names -from sqlbuild.compiler.references.main._compiled_project_relations import ( - compiled_project_relations, -) -from sqlbuild.compiler.references.main._old_name_reference_message import ( - old_name_reference_message, -) -from sqlbuild.compiler.references.main.extract_relation_names import extract_relation_names -from sqlbuild.compiler.references.main.hard_coded_relation_remedy import ( - hard_coded_relation_remedy, -) -from sqlbuild.compiler.references.main.match_project_relation import match_project_relation -from sqlbuild.compiler.references.models import ( - LiteralSqlRelation, - ProjectRelation, - ProjectRelationIndex, - RelationName, -) -from sqlbuild.compiler.references.types import HardCodedRelationOwnerKind -from sqlbuild.python_nodes.main.read_loader_definition import read_loader_definition -from sqlbuild.python_nodes.models import LoaderDefinition, SqlResourceRef -from sqlbuild.python_nodes.types import SqlResourceRefKind -from sqlbuild.spec.contracts.models import SourceLocation - -_SQL_METHOD_NAMES: frozenset[str] = frozenset({"query", "execute_sql"}) -_SQL_PARAMETER_NAME: str = "sql" -_CONTEXT_PARAMETER_NAMES: frozenset[str] = frozenset({"ctx", "context", "_ctx", "hook_context"}) -_VALUE_PLACEHOLDER: str = "__sqlbuild_python_value_" - - -@dataclass(frozen=True) -class _PythonSqlOwner: - label: str - kind: HardCodedRelationOwnerKind - function: Callable[..., object] - relative_path: Path - own_refs: frozenset[SqlResourceRef] = frozenset() - upstream_loader_by_source: Mapping[str, str] = field(default_factory=dict) - - -@dataclass(frozen=True) -class _LiteralSql: - method: str - sql: str - line: int - - -def python_sql_reference_diagnostics( - *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs -) -> PythonSqlReferenceReport: - """Return P008 errors for project relations literal SQL in Python code names.""" - - owners: tuple[_PythonSqlOwner, ...] = _owners( - project=project, discovered_inputs=discovered_inputs - ) - if not owners: - return PythonSqlReferenceReport() - index: ProjectRelationIndex = compiled_project_relations( - project=project, - old_name_retention=discovered_inputs.project_config.migrations.old_name_views, - ) - diagnostics: dict[tuple[str, int, str, str], CompilerDiagnostic] = {} - unmatched: list[LiteralSqlRelation] = [] - for owner in owners: - temporary: frozenset[str] = frozenset() - for literal in sorted(_literal_sql_calls(owner.function), key=lambda item: item.line): - found: dict[tuple[str, int, str, str], CompilerDiagnostic] - unnamed: tuple[LiteralSqlRelation, ...] - temporary, found, unnamed = _hard_coded_relations( - owner=owner, - literal=literal, - index=index, - dialect=project.sql_analysis_dialect, - temporary=temporary, - ) - unmatched.extend(unnamed) - for key, diagnostic in found.items(): - diagnostics.setdefault(key, diagnostic) - return PythonSqlReferenceReport( - diagnostics=tuple( - diagnostics[key] - for key in sorted( - diagnostics, - key=lambda key: (diagnostics[key].path or Path(), key[1], key[0], key[2], key[3]), - ) - ), - unmatched=tuple(dict.fromkeys(unmatched)), - ) - - -def _owners( - *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs -) -> tuple[_PythonSqlOwner, ...]: - owners: list[_PythonSqlOwner] = [] - for label, nodes in ( - ("task", discovered_inputs.task_functions), - ("asset", discovered_inputs.asset_functions), - ("check", discovered_inputs.check_functions), - ): - owners.extend( - _PythonSqlOwner( - label=f"{label}:{node.name}", - kind=HardCodedRelationOwnerKind.NODE, - function=node.function, - relative_path=node.relative_path, - ) - for node in nodes - ) - owners.extend(_loader_owners(project=project, discovered_inputs=discovered_inputs)) - attached_models: dict[str, set[SqlResourceRef]] = {} - for model in project.models: - for hook_name in model_python_hook_names(model=model): - attached_models.setdefault(hook_name, set()).add( - SqlResourceRef(kind=SqlResourceRefKind.MODEL, name=model.name) - ) - owners.extend( - _PythonSqlOwner( - label=f"hook:{hook.name}", - kind=HardCodedRelationOwnerKind.HOOK, - function=hook.function, - relative_path=hook.relative_path, - own_refs=frozenset(attached_models.get(hook.name, ())), - ) - for hook in project.hook_functions - ) - return tuple(owners) - - -def _loader_owners( - *, project: CompiledProject, discovered_inputs: DiscoveredProjectInputs -) -> tuple[_PythonSqlOwner, ...]: - sources_by_loader: dict[str, set[str]] = {} - for source in project.sources: - if source.source_entry.loader is not None: - sources_by_loader.setdefault(source.source_entry.loader, set()).add(source.name) - owners: list[_PythonSqlOwner] = [] - for loader in discovered_inputs.loader_functions: - upstream_loader_by_source: dict[str, str] = {} - for dependency in loader.depends_on: - definition: LoaderDefinition | None = ( - read_loader_definition(dependency) if callable(dependency) else None - ) - if definition is None: - continue - for source_name in sources_by_loader.get(definition.name, ()): - upstream_loader_by_source[source_name] = getattr( - dependency, "__name__", definition.name - ) - owners.append( - _PythonSqlOwner( - label=f"loader:{loader.name}", - kind=HardCodedRelationOwnerKind.LOADER, - function=loader.function, - relative_path=loader.relative_path, - own_refs=frozenset( - SqlResourceRef(kind=SqlResourceRefKind.SOURCE, name=source_name) - for source_name in sources_by_loader.get(loader.name, ()) - ), - upstream_loader_by_source=upstream_loader_by_source, - ) - ) - return tuple(owners) - - -def _literal_sql_calls(function: Callable[..., object]) -> Iterator[_LiteralSql]: - try: - lines: list[str] - start_line: int - lines, start_line = inspect.getsourcelines(function) - tree: ast.Module = ast.parse(textwrap.dedent("".join(lines))) - except (OSError, TypeError, SyntaxError): - return - definition: ast.stmt | None = tree.body[0] if tree.body else None - if not isinstance(definition, ast.FunctionDef | ast.AsyncFunctionDef): - return - parameters: list[str] = [ - argument.arg - for argument in ( - *definition.args.posonlyargs, - *definition.args.args, - *definition.args.kwonlyargs, - ) - ] - context_name: str | None = next( - (name for name in parameters if name in _CONTEXT_PARAMETER_NAMES), - parameters[0] if parameters else None, - ) - if context_name is None: - return - for node in ast.walk(definition): - if not ( - isinstance(node, ast.Call) - and isinstance(node.func, ast.Attribute) - and node.func.attr in _SQL_METHOD_NAMES - and isinstance(node.func.value, ast.Name) - and node.func.value.id == context_name - ): - continue - argument: ast.expr | None = _sql_argument(node) - sql: str | None = _literal_text(argument) if argument is not None else None - if sql is not None: - yield _LiteralSql(method=node.func.attr, sql=sql, line=start_line + node.lineno - 1) - - -def _sql_argument(call: ast.Call) -> ast.expr | None: - if call.args: - return call.args[0] - for keyword in call.keywords: - if keyword.arg == _SQL_PARAMETER_NAME: - return keyword.value - if keyword.arg is None and isinstance(keyword.value, ast.Dict): - for key, value in zip(keyword.value.keys, keyword.value.values, strict=True): - if isinstance(key, ast.Constant) and key.value == _SQL_PARAMETER_NAME: - return value - return None - - -def _literal_text(node: ast.expr) -> str | None: - if isinstance(node, ast.Constant) and isinstance(node.value, str): - return node.value - if not isinstance(node, ast.JoinedStr): - return None - parts: list[str] = [] - for index, value in enumerate(node.values): - if isinstance(value, ast.Constant) and isinstance(value.value, str): - parts.append(value.value) - else: - parts.append(f"{_VALUE_PLACEHOLDER}{index}") - return "".join(parts) - - -def _hard_coded_relations( - *, - owner: _PythonSqlOwner, - literal: _LiteralSql, - index: ProjectRelationIndex, - dialect: str | None, - temporary: frozenset[str], -) -> tuple[ - frozenset[str], - dict[tuple[str, int, str, str], CompilerDiagnostic], - tuple[LiteralSqlRelation, ...], -]: - extracted: tuple[tuple[RelationName, ...], frozenset[str]] | None = extract_relation_names( - sql=literal.sql, dialect=dialect - ) - found: dict[tuple[str, int, str, str], CompilerDiagnostic] = {} - unmatched: list[LiteralSqlRelation] = [] - if extracted is None: - return temporary, found, () - relations, created = extracted - temporary = temporary | created - for relation in relations: - if _VALUE_PLACEHOLDER in "".join( - part for part in (relation.database, relation.schema, relation.name) if part - ): - continue - if relation.schema is None and relation.name.casefold() in temporary: - continue - match: ProjectRelation | None = match_project_relation(index=index, relation=relation) - if match is None: - unmatched.append( - LiteralSqlRelation( - owner_label=owner.label, - owner_kind=owner.kind, - relative_path=owner.relative_path, - line=literal.line, - method=literal.method, - relation=relation, - ) - ) - continue - if match.ref in owner.own_refs and match.compatibility_for is None: - continue - key: tuple[str, int, str, str] = ( - owner.label, - literal.line, - match.ref.kind.value, - match.ref.name, - ) - found.setdefault( - key, - _hard_coded_relation_diagnostic( - owner=owner, literal=literal, relation=relation, match=match - ), - ) - return temporary, found, tuple(unmatched) - - -def _hard_coded_relation_diagnostic( - *, - owner: _PythonSqlOwner, - literal: _LiteralSql, - relation: RelationName, - match: ProjectRelation, -) -> CompilerDiagnostic: - written: str = ".".join( - part for part in (relation.database, relation.schema, relation.name) if part - ) - remedy: str = hard_coded_relation_remedy( - owner_kind=owner.kind, - ref=match.ref, - upstream_loader_by_source=owner.upstream_loader_by_source, - ) - qualify_help: str = ( - f"\n = help: if '{written}' is an external table that shares the name, qualify it " - "with its schema" - if relation.schema is None - else "" - ) - return CompilerDiagnostic( - phase=DiagnosticPhase.COMPILE, - severity=DiagnosticSeverity.ERROR, - code=HARD_CODED_PROJECT_RELATION_CODE, - message=( - old_name_reference_message( - owner_label=owner.label, - written=written, - method=literal.method, - model_name=match.compatibility_for, - ) - if match.compatibility_for is not None - else f"{owner.label} names {match.ref.kind.value}:{match.ref.name} as '{written}' " - f"in SQL passed to ctx.{literal.method}()" - ), - location=SourceLocation(path=owner.relative_path, line=literal.line, column=1), - help=( - f"{remedy}{qualify_help}\n" - " = help: " + explicit_references_help(allowed="hard-coded relation names") - ), - ) diff --git a/src/sqlbuild/compiler/compile/main/_assemble_project.py b/src/sqlbuild/compiler/compile/main/_assemble_project.py index e3fffde3ae..a165f9c226 100644 --- a/src/sqlbuild/compiler/compile/main/_assemble_project.py +++ b/src/sqlbuild/compiler/compile/main/_assemble_project.py @@ -8,21 +8,16 @@ from pathlib import Path from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile._helpers.assembly.audit_gates import ( - attached_audit_gate_diagnostics, -) from sqlbuild.compiler.compile._helpers.assembly.project import assemble_compiled_project from sqlbuild.compiler.compile._helpers.diagnostics.collector import ( with_collected_compile_diagnostics, ) -from sqlbuild.compiler.compile._helpers.explicit_references.hook_reads import hook_read_diagnostics -from sqlbuild.compiler.compile._helpers.explicit_references.python_sql import ( - python_sql_reference_diagnostics, +from sqlbuild.compiler.compile._helpers.explicit_references.checks import ( + explicit_reference_report, ) from sqlbuild.compiler.compile.models import ( CompiledProject, CompileProjectInputs, - CompilerDiagnostic, PythonSqlReferenceReport, ) from sqlbuild.compiler.lineage.types import ColumnLineageMode @@ -54,22 +49,15 @@ def assemble_project( project: CompiledProject = ( run_with_macro_bridge(stage=assemble) if active_macro_bridge() is None else assemble() ) - python_sql: PythonSqlReferenceReport = ( - python_sql_reference_diagnostics( - project=project, discovered_inputs=inputs.discovered_inputs - ) - if inputs.project_config.references.enforce_explicit - else PythonSqlReferenceReport() - ) - reference_diagnostics: tuple[CompilerDiagnostic, ...] = ( - *attached_audit_gate_diagnostics(project=project), - *hook_read_diagnostics(project=project), - *python_sql.diagnostics, + references: PythonSqlReferenceReport = explicit_reference_report( + project=project, + discovered_inputs=inputs.discovered_inputs, + enforce_explicit=inputs.project_config.references.enforce_explicit, ) - if not reference_diagnostics and not python_sql.unmatched: + if not references.diagnostics and not references.unmatched: return project return replace( project, - diagnostics=(*project.diagnostics, *reference_diagnostics), - unmatched_literal_sql_relations=python_sql.unmatched, + diagnostics=(*project.diagnostics, *references.diagnostics), + unmatched_literal_sql_relations=references.unmatched, ) diff --git a/src/sqlbuild/compiler/graph/_helpers/audit_gates.py b/src/sqlbuild/compiler/graph/_helpers/audit_gates.py index eb03e2d935..42df368146 100644 --- a/src/sqlbuild/compiler/graph/_helpers/audit_gates.py +++ b/src/sqlbuild/compiler/graph/_helpers/audit_gates.py @@ -6,8 +6,6 @@ from sqlbuild.compiler.compile.models import CompiledAudit, CompiledObjectKey, CompiledProject from sqlbuild.compiler.compile.types import AttachedAuditTargetKind, CompiledResourceType -from sqlbuild.compiler.graph._helpers.algorithms import transitive_closure_many_impl -from sqlbuild.compiler.graph._helpers.lineage import build_lineage_upstream_deps_impl from sqlbuild.compiler.graph.models import AttachedAuditGateEdge _READABLE_RESOURCE_TYPES: frozenset[CompiledResourceType] = frozenset( @@ -110,39 +108,3 @@ def _extra_reads( for read in audit.scope_deps if read != target and read.resource_type in _READABLE_RESOURCE_TYPES ) - - -def attached_audit_gate_cycles_impl( - *, project: CompiledProject -) -> tuple[AttachedAuditGateEdge, ...]: - """Return every attached-audit read that depends on the audit's own target.""" - - edges: tuple[AttachedAuditGateEdge, ...] = attached_audit_gate_edges_impl(project=project) - upstream: dict[CompiledObjectKey, list[CompiledObjectKey]] = { - key: list(deps) for key, deps in build_lineage_upstream_deps_impl(project).items() - } - cycles: dict[AttachedAuditGateEdge, None] = {} - edge: AttachedAuditGateEdge - for edge in edges: - upstream.setdefault(edge.gated, []).append(edge.read) - audit: CompiledAudit - for audit in sorted(project.audits, key=lambda item: item.name): - if audit.attached_target_kind is None or audit.attached_target_name is None: - continue - target: CompiledObjectKey = CompiledObjectKey( - resource_type=AttachedAuditTargetKind(audit.attached_target_kind).resource_type, - name=audit.attached_target_name, - ) - read: CompiledObjectKey - for read in _extra_reads(audit=audit, target=target): - if target in transitive_closure_many_impl( - starts=(read,), edges=upstream, include_starts=False - ): - cycles[ - AttachedAuditGateEdge( - audit_name=audit.name, target=target, gated=target, read=read - ) - ] = None - return tuple( - sorted(cycles, key=lambda item: (item.audit_name, item.target.name, item.read.name)) - ) diff --git a/src/sqlbuild/compiler/graph/_helpers/hook_reads.py b/src/sqlbuild/compiler/graph/_helpers/hook_reads.py index c072752a17..ec222b1b90 100644 --- a/src/sqlbuild/compiler/graph/_helpers/hook_reads.py +++ b/src/sqlbuild/compiler/graph/_helpers/hook_reads.py @@ -4,11 +4,8 @@ from pathlib import Path -from sqlbuild.compiler.compile.models import CompiledModel, CompiledObjectKey, CompiledProject +from sqlbuild.compiler.compile.models import CompiledModel, CompiledProject from sqlbuild.compiler.discovery.models import PythonHookEntry, SqlHookEntry -from sqlbuild.compiler.graph._helpers.algorithms import transitive_closure_many_impl -from sqlbuild.compiler.graph._helpers.audit_gates import attached_audit_gate_edges_impl -from sqlbuild.compiler.graph._helpers.lineage import build_lineage_upstream_deps_impl from sqlbuild.compiler.graph._helpers.sql_refs import sql_ref_key_impl from sqlbuild.compiler.graph.models import HookReadEdge from sqlbuild.compiler.graph.types import HookReadType @@ -77,26 +74,3 @@ def _sql_hook_read_edges(model: CompiledModel) -> tuple[HookReadEdge, ...]: for ref in entry.reads ) return tuple(edges) - - -def hook_read_cycles_impl(*, project: CompiledProject) -> tuple[HookReadEdge, ...]: - """Return every declared hook read that depends on the model running the hook.""" - - edges: tuple[HookReadEdge, ...] = hook_read_edges_impl(project=project) - if not edges: - return () - upstream: dict[CompiledObjectKey, list[CompiledObjectKey]] = { - key: list(deps) for key, deps in build_lineage_upstream_deps_impl(project).items() - } - for gate in attached_audit_gate_edges_impl(project=project): - upstream.setdefault(gate.gated, []).append(gate.read) - for edge in edges: - upstream.setdefault(edge.gated, []).append(edge.read) - return tuple( - edge - for edge in sorted( - edges, key=lambda item: (item.gated.name, item.hook_name, item.read.name) - ) - if edge.gated - in transitive_closure_many_impl(starts=(edge.read,), edges=upstream, include_starts=False) - ) diff --git a/src/sqlbuild/compiler/graph/main/_attached_audit_gate_cycles.py b/src/sqlbuild/compiler/graph/main/_attached_audit_gate_cycles.py deleted file mode 100644 index 2d117e43f3..0000000000 --- a/src/sqlbuild/compiler/graph/main/_attached_audit_gate_cycles.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Attached-audit gate cycle detection entrypoint.""" - -from __future__ import annotations - -from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.graph._helpers.audit_gates import attached_audit_gate_cycles_impl -from sqlbuild.compiler.graph.models import AttachedAuditGateEdge - - -def attached_audit_gate_cycles(*, project: CompiledProject) -> tuple[AttachedAuditGateEdge, ...]: - """Return every attached-audit read that depends on the audit's own target.""" - - return attached_audit_gate_cycles_impl(project=project) diff --git a/src/sqlbuild/compiler/graph/main/_hook_read_cycles.py b/src/sqlbuild/compiler/graph/main/_hook_read_cycles.py deleted file mode 100644 index 69c6e541a4..0000000000 --- a/src/sqlbuild/compiler/graph/main/_hook_read_cycles.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Python hook read cycle detection entrypoint.""" - -from __future__ import annotations - -from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.graph._helpers.hook_reads import hook_read_cycles_impl -from sqlbuild.compiler.graph.models import HookReadEdge - - -def hook_read_cycles(*, project: CompiledProject) -> tuple[HookReadEdge, ...]: - """Return every declared hook read that depends on the model running the hook.""" - - return hook_read_cycles_impl(project=project) diff --git a/src/sqlbuild/compiler/references/_helpers/project_relations.py b/src/sqlbuild/compiler/references/_helpers/project_relations.py index d634ef504a..28c96a92f0 100644 --- a/src/sqlbuild/compiler/references/_helpers/project_relations.py +++ b/src/sqlbuild/compiler/references/_helpers/project_relations.py @@ -4,25 +4,11 @@ from collections.abc import Iterable, Mapping -from sqlbuild.compiler.compile.constants import MIGRATE_FROM_CONFIG_KEY -from sqlbuild.compiler.compile.models import ( - CompiledModel, - CompiledProject, - CompiledRelationLocation, -) -from sqlbuild.compiler.migrations.main._resolve_old_name_view_retention import ( - resolve_old_name_view_retention, -) from sqlbuild.compiler.references._helpers.relation_names import ( extract_relation_names_impl, - match_project_relation_impl, ) from sqlbuild.compiler.references.models import ProjectRelation, ProjectRelationIndex, RelationName from sqlbuild.python_nodes.models import SqlResourceRef -from sqlbuild.python_nodes.types import SqlResourceRefKind -from sqlbuild.spec.contracts.models import SourceEntry - -_SCHEMA_QUALIFIED_PARTS: int = 2 def build_project_relation_index_impl( @@ -33,92 +19,6 @@ def build_project_relation_index_impl( return ProjectRelationIndex(relations=tuple(dict.fromkeys(relations))) -def compiled_project_relations_impl( - *, project: CompiledProject, old_name_retention: str | None -) -> ProjectRelationIndex: - """Return the project relations a compiled project declares, with declared old names.""" - - relations: list[ProjectRelation] = [ - _location_relation( - ref=SqlResourceRef(kind=SqlResourceRefKind.MODEL, name=model.name), - location=model.destination, - ) - for model in project.models - ] - relations.extend( - _location_relation( - ref=SqlResourceRef(kind=SqlResourceRefKind.SEED, name=seed.name), - location=seed.destination, - ) - for seed in project.seeds - ) - for source in project.sources: - entry: SourceEntry = source.source_entry - if entry.expression is not None: - continue - relations.append( - ProjectRelation( - ref=SqlResourceRef(kind=SqlResourceRefKind.SOURCE, name=source.name), - relation=RelationName( - name=entry.table or entry.name, schema=entry.schema, database=entry.database - ), - ) - ) - declared: ProjectRelationIndex = build_project_relation_index_impl(relations) - return build_project_relation_index_impl( - ( - *declared.relations, - *_declared_old_names( - project=project, declared=declared, old_name_retention=old_name_retention - ), - ) - ) - - -def _declared_old_names( - *, project: CompiledProject, declared: ProjectRelationIndex, old_name_retention: str | None -) -> tuple[ProjectRelation, ...]: - """Return old names that migrate_from declarations keep only as compatibility views.""" - - model_names: frozenset[str] = frozenset(model.name for model in project.models) - old_names: list[ProjectRelation] = [] - model: CompiledModel - for model in project.models: - raw: object = model.config.values.get(MIGRATE_FROM_CONFIG_KEY) - if not isinstance(raw, str) or ( - resolve_old_name_view_retention( - config_values=model.config.values, project_retention=old_name_retention - ) - is None - ): - continue - parts: list[str] = [part.strip().strip('"`') for part in raw.split(".")] - if len(parts) == 1 and parts[0] in model_names: - continue - relation: RelationName = RelationName( - name=parts[-1], - schema=parts[-2] if len(parts) > 1 else model.destination.schema, - database=parts[-3] - if len(parts) > _SCHEMA_QUALIFIED_PARTS - else model.destination.database, - ) - if ( - match_project_relation_impl( - index=declared, relation=relation, default_database=None, default_schema=None - ) - is not None - ): - continue - old_names.append( - ProjectRelation( - ref=SqlResourceRef(kind=SqlResourceRefKind.MODEL, name=model.name), - relation=relation, - compatibility_for=model.name, - ) - ) - return tuple(old_names) - - def runtime_project_relations_impl( *, relations: Mapping[SqlResourceRef, str], dialect: str | None ) -> ProjectRelationIndex: @@ -133,14 +33,3 @@ def runtime_project_relations_impl( continue resolved.append(ProjectRelation(ref=ref, relation=extracted[0][0])) return build_project_relation_index_impl(resolved) - - -def _location_relation( - *, ref: SqlResourceRef, location: CompiledRelationLocation -) -> ProjectRelation: - return ProjectRelation( - ref=ref, - relation=RelationName( - name=location.name, schema=location.schema, database=location.database - ), - ) diff --git a/src/sqlbuild/compiler/references/main/_compiled_project_relations.py b/src/sqlbuild/compiler/references/main/_compiled_project_relations.py deleted file mode 100644 index 317abacd55..0000000000 --- a/src/sqlbuild/compiler/references/main/_compiled_project_relations.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Compile-time project-relation index entrypoint.""" - -from __future__ import annotations - -from sqlbuild.compiler.compile.models import CompiledProject -from sqlbuild.compiler.references._helpers.project_relations import compiled_project_relations_impl -from sqlbuild.compiler.references.models import ProjectRelationIndex - - -def compiled_project_relations( - *, project: CompiledProject, old_name_retention: str | None -) -> ProjectRelationIndex: - """Return the project relations a compiled project declares, with declared old names.""" - - return compiled_project_relations_impl(project=project, old_name_retention=old_name_retention) diff --git a/tests/goldens/python_oracle/explicit_references_edge_projects.json b/tests/goldens/python_oracle/explicit_references_edge_projects.json new file mode 100644 index 0000000000..5116d3538a --- /dev/null +++ b/tests/goldens/python_oracle/explicit_references_edge_projects.json @@ -0,0 +1,54 @@ +[ +[ +"explicit_references", +"b70b8e802a4b67adeb1fc2e3" +], +[ +"explicit_references", +"1f2e788d4349e0a43ed9d99e" +], +[ +"explicit_references", +"21233f8423253e48e8844b06" +], +[ +"explicit_references", +"8a8030c53ec888f2b6a28cc4" +], +[ +"explicit_references", +"18216c9b230d59f93e117e5b" +], +[ +"explicit_references", +"204c1570007330f19d3d5ef1" +], +[ +"explicit_references", +"d41afb90907fa281b173024c" +], +[ +"explicit_references", +"34b91f2790f83b0b7d3d4820" +], +[ +"explicit_references", +"a9461cb64efcfc87f387dd4e" +], +[ +"explicit_references", +"92e4d194703692bbf96d85a3" +], +[ +"explicit_references", +"7e3caa377591a83b80f4d859" +], +[ +"explicit_references", +"aec22e4d642969f541fa5af9" +], +[ +"explicit_references", +"c16aa298ae2448053bd5e47a" +] +] diff --git a/tests/integration/src/sqlbuild/compiler/explicit_references/__init__.py b/tests/integration/src/sqlbuild/compiler/explicit_references/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/src/sqlbuild/compiler/explicit_references/_test_types.py b/tests/integration/src/sqlbuild/compiler/explicit_references/_test_types.py new file mode 100644 index 0000000000..51f33a25d8 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/explicit_references/_test_types.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ExplicitReferenceGoldenTestCase: + """Projects whose explicit-reference outcomes must equal Python's recorded golden.""" + + description: str + projects: tuple[tuple[str, dict[str, str], str], ...] + expected_codes: frozenset[str] + expected_raised: int diff --git a/tests/integration/src/sqlbuild/compiler/explicit_references/helpers.py b/tests/integration/src/sqlbuild/compiler/explicit_references/helpers.py new file mode 100644 index 0000000000..79e719e046 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/explicit_references/helpers.py @@ -0,0 +1,438 @@ +"""Projects whose explicit-reference checks raise or report, and their full compile outcomes.""" + +from __future__ import annotations + +from collections.abc import Callable +from operator import attrgetter +from pathlib import Path +from typing import Any, cast + +from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter +from sqlbuild.compiler.compile.main._assemble_project import assemble_project +from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs +from sqlbuild.compiler.compile.models import ( + CompileAdapterContext, + CompiledProject, + CompilerDiagnostic, + LiteralSqlRelation, +) +from sqlbuild.compiler.discovery.main.discover import discover_project_inputs +from sqlbuild.sql_values.types import CollectionRendering + +_ADAPTER_CONTEXT: CompileAdapterContext = CompileAdapterContext( + value_renderer=DuckDbAdapter(), + collection_rendering=CollectionRendering.VALUE_LIST, + python_functions_inherit_default_namespace=True, + sql_lexical_syntax=DuckDbAdapter.sql_lexical_syntax, +) +_PROJECT: str = ( + 'name = "orders_project"\nadapter = "duckdb"\n\n' + "[settings]\nsql_analysis = false\nsql_validation = false\n" +) +_DISABLED: str = _PROJECT + "\n[references]\nenforce_explicit = false\n" +_OLD_NAMES_OFF: str = _PROJECT + "\n[migrations]\nold_name_views = false\n" +_HOOK_PATH: str = "models/marts/_sqlbuild/_hooks/python/lookups.py" +_SQL_HOOK_PATH: str = "models/marts/_sqlbuild/_hooks/sql/record_rows.sql" +_TASK_PATH: str = "python/tasks/export.py" +_CHECK_PATH: str = "python/checks/orders.py" +_ASSET_PATH: str = "python/assets/snapshot.py" +_LOADER_PATH: str = "python/loaders/raw.py" +_HOOKED: str = 'MODEL (description "Orders.", post_hooks [python("refresh_lookup")]);' +_NAMED_SQL_HOOK: str = 'MODEL (description "Orders.", post_hooks [sql("record_rows")]);' + + +def _model(sql: str, header: str = 'MODEL (description "Test model.");') -> str: + return f"{header}\n{sql}\n" + + +def _hook( + *, reads: str = "()", body: str = " return None\n", name: str = "refresh_lookup" +) -> str: + return ( + "from sqlbuild.hooks import hook\nfrom sqlbuild.refs import model, seed, source\n\n\n" + f"@hook(reads={reads})\ndef {name}(ctx):\n{body}" + ) + + +def _task(body: str, depends_on: str = 'model("orders")') -> str: + return ( + "from sqlbuild.refs import model, source\nfrom sqlbuild.tasks import task\n\n\n" + f"@task(depends_on={depends_on})\ndef export_orders(ctx):\n{body}" + ) + + +def _check(body: str) -> str: + return ( + "from sqlbuild.checks import check\nfrom sqlbuild.refs import model\n\n\n" + '@check(depends_on=model("orders"))\ndef orders_present(ctx):\n' + f"{body} return ctx.pass_()\n" + ) + + +def _asset(body: str) -> str: + return ( + "from sqlbuild.assets import asset\nfrom sqlbuild.refs import model\n\n\n" + '@asset(depends_on=model("orders"))\ndef orders_snapshot(context):\n' + f' """Test asset."""\n{body} return None\n' + ) + + +def _loaders(*, depends_on: str, body: str) -> str: + return ( + "from sqlbuild.loaders import loader\n\n\n" + "@loader\ndef raw_regions(ctx):\n '''Test loader raw_regions.'''\n" + " return [{'id': 1}]\n\n\n" + f"@loader(depends_on=[{depends_on}])\ndef raw_customers(ctx):\n{body}" + " return [{'id': 1}]\n" + ) + + +_MODELS: dict[str, str] = { + "models/marts/customers.sql": _model("SELECT 1 AS customer_id"), + "models/marts/orders.sql": _model("SELECT 1 AS order_id", header=_HOOKED), +} +_SEED: dict[str, str] = { + "seeds/country_codes.yml": ( + "seeds:\n - name: country_codes\n description: Country codes.\n" + " columns:\n - name: code\n type: VARCHAR\n" + ), + "seeds/country_codes.csv": "code\nGB\n", +} +_MANAGED: dict[str, str] = { + "sources/raw.yml": ( + "sources:\n" + " - name: raw_regions\n description: Raw regions.\n managed: true\n" + " write_strategy: table\n columns:\n - name: id\n type: INTEGER\n" + " - name: raw_customers\n description: Raw customers.\n managed: true\n" + " write_strategy: table\n columns:\n - name: id\n type: INTEGER\n" + ), +} +_EXTERNAL: dict[str, str] = { + "sources/landing.yml": ( + "sources:\n - name: landing_orders\n description: Landing orders.\n" + " schema: landing\n table: orders_feed\n" + " - name: computed\n description: Computed.\n expression: SELECT 1 AS id\n" + ), +} +_GATED_AUDIT: str = "AUDIT ();\nSELECT s.* FROM @relation s LEFT JOIN {read} a USING (code)" + + +def _gate_model(sql: str, audits: str) -> str: + return _model(sql, header=f'MODEL (description "Gated.", audits [{audits}]);') + + +# (description, files, project toml) +EXPLICIT_REFERENCE_PROJECTS: tuple[tuple[str, dict[str, str], str], ...] = ( + ( + "task literal SQL reading a managed source and a model", + { + **_MODELS, + **_MANAGED, + _HOOK_PATH: _hook(), + _LOADER_PATH: _loaders(depends_on="", body=""), + _TASK_PATH: _task( + ' ctx.query("SELECT * FROM raw_customers JOIN customers ON true")\n' + ' ctx.execute_sql(sql="DELETE FROM main.orders")\n' + ), + }, + _PROJECT, + ), + ( + "every owner kind, dedup on one line and line ordering across files", + { + **_MODELS, + **_SEED, + _HOOK_PATH: _hook( + reads='model("customers")', + body=( + ' ctx.query("SELECT * FROM orders JOIN customers ON true")\n' + ' ctx.query("SELECT * FROM country_codes, COUNTRY_CODES")\n' + ), + ), + _TASK_PATH: _task( + ' ctx.query("SELECT * FROM customers c1 JOIN customers c2 ON true")\n' + " ctx.query(**{'sql': 'SELECT * FROM \"Orders\"'})\n" + ), + _CHECK_PATH: _check(' ctx.query("SELECT count(*) FROM memory.main.customers")\n'), + _ASSET_PATH: _asset(' context.query("SELECT 1 FROM orders")\n'), + }, + _PROJECT, + ), + ( + "temporary tables, f-string placeholders, external and unparseable SQL", + { + **_MODELS, + **_EXTERNAL, + _HOOK_PATH: _hook(), + _TASK_PATH: _task( + ' ctx.query("CREATE TEMP TABLE customers AS SELECT 1")\n' + ' ctx.query("SELECT * FROM customers JOIN main.customers ON true")\n' + ' ctx.query(f"SELECT * FROM {ctx.schema}.orders ' + 'JOIN warehouse.events ON true")\n' + ' ctx.query(f"SELECT * FROM orders_{1} JOIN landing.orders_feed ON true")\n' + ' ctx.query("SELECT * FROM computed JOIN external_lookup ON true")\n' + ' ctx.query("SELEC broken FROM (")\n' + " ctx.query(sql_text)\n" + ), + }, + _PROJECT, + ), + ( + "loaders read their own, upstream and other sources and models", + { + **_MODELS, + **_MANAGED, + **_SEED, + _HOOK_PATH: _hook(), + _LOADER_PATH: _loaders( + depends_on="raw_regions", + body=( + ' ctx.query("SELECT * FROM raw_customers JOIN raw_regions ON true")\n' + ' ctx.query("SELECT * FROM customers JOIN country_codes ON true")\n' + ), + ), + }, + _PROJECT, + ), + ( + "loader with no upstream loader reads another managed source", + { + **_MODELS, + **_MANAGED, + _HOOK_PATH: _hook(), + _LOADER_PATH: _loaders( + depends_on="", + body=' ctx.query("SELECT * FROM raw_regions")\n', + ), + }, + _PROJECT, + ), + ( + "old names kept as compatibility views", + { + "models/marts/customers.sql": _model( + "SELECT 1 AS customer_id", + header=( + 'MODEL (description "Customers.", materialized table, ' + "migrate_from legacy_customers);" + ), + ), + "models/marts/orders.sql": _model( + "SELECT 1 AS order_id", + header=( + 'MODEL (description "Orders.", materialized table, ' + 'migrate_from "archive.old_orders");' + ), + ), + "models/marts/regions.sql": _model( + "SELECT 1 AS region_id", + header=( + 'MODEL (description "Regions.", materialized table, migrate_from customers);' + ), + ), + _TASK_PATH: _task( + ' ctx.query("SELECT * FROM legacy_customers JOIN archive.old_orders ON true")\n' + ' ctx.query("SELECT * FROM main.LEGACY_CUSTOMERS")\n' + ), + }, + _PROJECT, + ), + ( + "old names off leave old relations unmatched", + { + "models/marts/customers.sql": _model( + "SELECT 1 AS customer_id", + header=( + 'MODEL (description "Customers.", materialized table, ' + "migrate_from legacy_customers);" + ), + ), + "models/marts/orders.sql": _model("SELECT 1 AS order_id"), + _TASK_PATH: _task(' ctx.query("SELECT * FROM legacy_customers")\n'), + }, + _OLD_NAMES_OFF, + ), + ( + "enforcement disabled still checks hooks and audits", + { + **_MODELS, + _HOOK_PATH: _hook(reads='model("customers")'), + _TASK_PATH: _task(' ctx.query("SELECT * FROM customers")\n'), + }, + _DISABLED, + ), + ( + "python hook declaring an unknown model read", + { + **_MODELS, + _HOOK_PATH: _hook(reads='model("missing_customers")'), + _TASK_PATH: _task(' ctx.query("SELECT * FROM customers")\n'), + }, + _PROJECT, + ), + ( + "unknown hook reads raise for the first hook by name", + { + **_MODELS, + _HOOK_PATH: ( + _hook(reads='(seed("nope"), model("customers"))', name="z_hook") + + "\n\n" + + _hook(reads='source("missing")', name="a_hook").split("\n\n\n", 1)[1] + + "\n\n" + + _hook(name="refresh_lookup").split("\n\n\n", 1)[1] + ), + }, + _PROJECT, + ), + ( + "python and sql hooks reading their own dependants", + { + _HOOK_PATH: _hook(reads='model("customers")'), + _SQL_HOOK_PATH: ( + 'HOOK (description "Records rows.");\n' + 'INSERT INTO audit_log SELECT count(*) FROM __ref("item_totals")\n' + ), + "models/marts/orders.sql": _model("SELECT 1 AS order_id", header=_HOOKED), + "models/marts/customers.sql": _model('SELECT * FROM __ref("orders")'), + "models/marts/items.sql": _model("SELECT 1 AS item_id", header=_NAMED_SQL_HOOK), + "models/marts/item_totals.sql": _model( + 'SELECT * FROM __ref("items")', + header=( + 'MODEL (description "Totals.", pre_hooks ' + "[inline_sql('SELECT * FROM __ref(\"item_rollup\")')]);" + ), + ), + "models/marts/item_rollup.sql": _model('SELECT * FROM __ref("item_totals")'), + }, + _PROJECT, + ), + ( + "attached audits reading their targets' dependants", + { + **_SEED, + **_EXTERNAL, + "audits/generic/gated_lookup.sql": _GATED_AUDIT.format(read='__ref("enriched")'), + "audits/generic/source_lookup.sql": _GATED_AUDIT.format(read='__ref("landing_view")'), + "models/marts/base.sql": _gate_model("SELECT 'GB' AS code", "gated_lookup"), + "models/marts/enriched.sql": _model('SELECT * FROM __ref("base")'), + "audits/generic/seed_lookup.sql": _GATED_AUDIT.format(read='__ref("coded")'), + "models/marts/landing_view.sql": _model('SELECT * FROM __source("landing_audited")'), + "models/marts/coded.sql": _model('SELECT * FROM __seed("audited_codes")'), + "sources/landing_audits.yml": ( + "sources:\n - name: landing_audited\n description: Landing audited.\n" + " schema: landing\n audits:\n - source_lookup\n" + ), + "seeds/audited_codes.yml": ( + "seeds:\n - name: audited_codes\n description: Audited codes.\n" + " columns:\n - name: code\n type: VARCHAR\n" + " audits:\n - seed_lookup\n" + ), + "seeds/audited_codes.csv": "code\nGB\n", + }, + _PROJECT, + ), + ( + "audit gates, hook cycles and hard-coded names together", + { + "audits/generic/gated_lookup.sql": _GATED_AUDIT.format(read='__ref("enriched")'), + "models/marts/base.sql": _gate_model("SELECT 'GB' AS code", "gated_lookup"), + "models/marts/enriched.sql": _model('SELECT * FROM __ref("base")'), + _HOOK_PATH: _hook( + reads='model("customers")', + body=' ctx.query("SELECT * FROM legacy_orders JOIN orders ON true")\n', + ), + "models/marts/orders.sql": _model( + "SELECT 1 AS order_id", + header=( + 'MODEL (description "Orders.", materialized table, ' + 'migrate_from legacy_orders, post_hooks [python("refresh_lookup")]);' + ), + ), + "models/marts/customers.sql": _model('SELECT * FROM __ref("orders")'), + _TASK_PATH: _task(' ctx.query("SELECT * FROM enriched")\n'), + }, + _PROJECT, + ), +) + + +def write_project(*, project_dir: Path, files: dict[str, str], toml: str) -> None: + """Write the project config and files below `project_dir`.""" + + _ = [ + _write(path=project_dir / relative, contents=contents) + for relative, contents in {"sqlbuild_project.toml": toml, **files}.items() + ] + + +def compile_outcome(*, project_dir: Path) -> tuple[object, ...]: + """Assemble the project: every diagnostic and unmatched relation, or the error raised.""" + + try: + project: CompiledProject = assemble_project( + inputs=build_compile_inputs( + discovered_inputs=discover_project_inputs(project_dir=project_dir), + adapter_context=_ADAPTER_CONTEXT, + run_id="explicit_references", + ), + skip_column_inference=True, + ) + except Exception as error: + return ( + "error", + type(error).__name__, + str(error), + getattr(error, "code", None), + getattr(error, "help", None), + ) + return ( + "ok", + tuple(_diagnostic_view(diagnostic) for diagnostic in project.diagnostics), + tuple(_unmatched_view(relation) for relation in project.unmatched_literal_sql_relations), + ) + + +def _diagnostic_view(diagnostic: CompilerDiagnostic) -> tuple[object, ...]: + return ( + str(diagnostic.phase), + str(diagnostic.severity), + diagnostic.code, + diagnostic.message, + _optional(diagnostic.path, Path.as_posix), + diagnostic.line, + _optional(diagnostic.location, attrgetter("column")), + _optional(diagnostic.resource_type, str), + diagnostic.resource_name, + diagnostic.help, + ) + + +def _optional(value: Any, convert: Callable[[Any], object]) -> object: + """`convert(value)`, or None where `value` is None.""" + + return {True: lambda: None, False: lambda: convert(value)}[value is None]() + + +def outcome_codes(outcome: tuple[object, ...]) -> tuple[object, ...]: + """The diagnostic codes of a successful outcome; none for a raised error.""" + + return { + "ok": lambda: tuple(diagnostic[2] for diagnostic in cast(Any, outcome[1])), + "error": tuple, + }[cast(str, outcome[0])]() + + +def _unmatched_view(relation: LiteralSqlRelation) -> tuple[object, ...]: + return ( + relation.owner_label, + str(relation.owner_kind), + relation.relative_path.as_posix(), + relation.line, + relation.method, + (relation.relation.database, relation.relation.schema, relation.relation.name), + ) + + +def _write(*, path: Path, contents: str) -> int: + path.parent.mkdir(parents=True, exist_ok=True) + return path.write_text(contents, encoding="utf-8") diff --git a/tests/integration/src/sqlbuild/compiler/explicit_references/test_native_explicit_references.py b/tests/integration/src/sqlbuild/compiler/explicit_references/test_native_explicit_references.py new file mode 100644 index 0000000000..a25d35f57d --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/explicit_references/test_native_explicit_references.py @@ -0,0 +1,69 @@ +"""Native explicit-reference checks report Python's recorded diagnostics, order and errors.""" + +from __future__ import annotations + +from itertools import chain +from pathlib import Path + +import pytest + +from tests.integration.src.sqlbuild.compiler.explicit_references._test_types import ( + ExplicitReferenceGoldenTestCase, +) +from tests.integration.src.sqlbuild.compiler.explicit_references.helpers import ( + EXPLICIT_REFERENCE_PROJECTS, + compile_outcome, + outcome_codes, + write_project, +) +from tests.integration.src.sqlbuild.compiler.helpers import ( + GoldenEntry, + golden_differences, + golden_entry, + golden_name, + read_golden, +) + + +@pytest.mark.parametrize( + "test_case", + [ + ExplicitReferenceGoldenTestCase( + description="edge projects", + projects=EXPLICIT_REFERENCE_PROJECTS, + expected_codes=frozenset({"P005", "P007", "P008"}), + expected_raised=2, + ) + ], + ids=lambda case: case.description, +) +def test_given_reference_edge_projects_when_compiling_then_matches_python_golden( + test_case: ExplicitReferenceGoldenTestCase, tmp_path: Path +) -> None: + project_dirs: list[Path] = [ + tmp_path / f"project_{index}" for index in range(len(test_case.projects)) + ] + _ = [ + write_project(project_dir=project_dir, files=files, toml=toml) + for project_dir, (_, files, toml) in zip(project_dirs, test_case.projects, strict=True) + ] + outcomes: list[tuple[object, ...]] = [ + compile_outcome(project_dir=project_dir) for project_dir in project_dirs + ] + golden: list[GoldenEntry] = [ + golden_entry("explicit_references", outcome, masked=(str(project_dir),)) + for project_dir, outcome in zip(project_dirs, outcomes, strict=True) + ] + codes: frozenset[object] = frozenset(chain.from_iterable(map(outcome_codes, outcomes))) + + assert ( + golden_differences( + read_golden(golden_name("explicit_references", test_case.description)), golden + ), + test_case.expected_codes <= codes, + sum(outcome[0] == "error" for outcome in outcomes), + ) == ([], True, test_case.expected_raised) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) From 2b6744b8c8e2042560dc6d3563d3ac328ba80bee Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:42:59 +0100 Subject: [PATCH 35/36] fix(native): preserve POSIX paths in unknown hook-read diagnostics --- .../compiler/compile/_helpers/explicit_references/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py b/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py index 7a0971e489..e86357b808 100644 --- a/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py +++ b/src/sqlbuild/compiler/compile/_helpers/explicit_references/checks.py @@ -114,7 +114,7 @@ def explicit_reference_report( "hooks": [ { "name": hook.name, - "path": str(hook.relative_path), + "path": hook.relative_path.as_posix(), "reads": _ref_rows(hook.reads), } for hook in project.hook_functions From 04462d5a469190ae9aa8f09b46f771edbc07ff2a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 11 Oct 2026 02:17:03 +0100 Subject: [PATCH 36/36] test: align compiler regression instrumentation with native APIs --- tests/e2e/scripts/compiler_differential/helpers.py | 8 +++++--- .../test_compiler_differential.py | 4 ++-- .../test_native_fallback_gate.py | 8 ++++---- .../cli/commands/main/compile/_test_types.py | 3 +-- .../sqlbuild/cli/commands/main/compile/helpers.py | 12 +----------- .../main/compile/test_compile_incremental_edits.py | 6 +----- .../commands/main/compile/test_native_type_system.py | 2 +- 7 files changed, 15 insertions(+), 28 deletions(-) diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 0be911fcd0..117529835c 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -99,10 +99,12 @@ def _perturbed(self, **changes): _record.write(json.dumps({"kind": "legacy_fallback", "site": "orders.sql"}) + "\\n") """ -MODEL_ANALYSIS_SABOTAGE: str = """ -import sqlbuild._native as native +FALLBACK_RECORD_PERTURBATION: str = """ +from sqlbuild.compiler.frontier._helpers.process_recorder import process_recorder -native.start_model_analysis_session = lambda *_args: None +recorder = process_recorder() +if recorder is not None: + recorder.record(site="analysis_session", kind="session") """ _BROKEN_REF_FILES: dict[str, str] = { diff --git a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py index d0a0ba685a..668aae3025 100644 --- a/tests/e2e/scripts/compiler_differential/test_compiler_differential.py +++ b/tests/e2e/scripts/compiler_differential/test_compiler_differential.py @@ -382,8 +382,8 @@ def test_given_failure_expectation_when_code_is_not_the_first_error_then_harness ), expected_sites=frozenset( { - "compiler/compile/_helpers/analysis/validation.py:" - "_validate_sql_syntax_with_message parse_one", + "compiler/compile/_helpers/analysis/required_columns.py:" + "resolve_required_external_columns parse_one", } ), expected_deferrals=frozenset(), diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 464f38c5ee..ad31837751 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -9,7 +9,7 @@ from scripts.compiler_differential.main.differential import run_compiler_differential from tests.e2e.scripts.compiler_differential._test_types import NativeFallbackGateTestCase from tests.e2e.scripts.compiler_differential.helpers import ( - MODEL_ANALYSIS_SABOTAGE, + FALLBACK_RECORD_PERTURBATION, harness_arguments, write_native_perturbation, ) @@ -42,12 +42,12 @@ ), ), NativeFallbackGateTestCase( - description="preview_model_analysis_sabotaged_to_python_fails", - perturbation=MODEL_ANALYSIS_SABOTAGE, + description="unexpected_preview_fallback_record_fails", + perturbation=FALLBACK_RECORD_PERTURBATION, appended_entries="", expected_exit_code=1, expected_lines=( - "Native fallback allow-list: native-preview model_analysis analysis_session " + "Native fallback allow-list: native-preview analysis_session analysis_session " "session (project): ", "Compiler differential FAILED: 0 of 1 projects differ", ), diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py index 3474593727..2b3efe39ea 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py @@ -797,10 +797,9 @@ class RetiredCompilerCacheTestCase: @dataclass(frozen=True) class SharedCacheKeyTestCase: - """A model analysis schedule under which two models share one analysis cache key.""" + """Expected parity when identical SQL models share native cached analysis.""" description: str - schedule: Callable[[pytest.MonkeyPatch], None] expected_matches_uncached: bool = True diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py index 5cc72053ff..5f86309c58 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py @@ -35,7 +35,6 @@ import sqlbuild.cli.compile_reuse._helpers.attempt as reuse_attempt import sqlbuild.cli.compile_reuse._helpers.project_files as reuse_project_files import sqlbuild.cli.compile_reuse._helpers.store as reuse_store -import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.diagnostics.recovery as diagnostic_recovery import sqlbuild.compiler.compile._helpers.macro_bridge.call_store as call_store_module import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stages @@ -3879,12 +3878,6 @@ def twin_header_changed(root: Path) -> None: ) -def analyze_in_one_batch(monkeypatch: pytest.MonkeyPatch) -> None: - """Analyze every model in one batch, without dependency-ordered binding.""" - - monkeypatch.setattr(project_assembly, "binding_schema_for_model", lambda **_kwargs: None) - - def star_chain_added(root: Path) -> None: """Add two models that each select every column of the one before, below staging orders.""" @@ -4952,11 +4945,10 @@ def engine_in_process_compile( class NativeTypeAnswers(NamedTuple): """What the native type system answered in one compile: each Python-side normalization, - contract results that compared at least one typed column, and contract handbacks.""" + and contract results that compared at least one typed column.""" normalized: list[bool] typed_comparisons: int - handbacks: int def type_system_engine_compile( @@ -4983,7 +4975,6 @@ def recorded(type_sql: str, dialect: str) -> object: def recorded_contracts(request: NativeContractRequest) -> list[NativeContractOutcome]: outcomes: list[NativeContractOutcome] = evaluate_contracts(request) contract_statuses.update(native_contract_statuses(request=request, outcomes=outcomes)) - contract_statuses["handbacks"] += sum(deferral is not None for deferral, _ in outcomes) return outcomes type_normalization.normalize_type.cache_clear() @@ -4997,7 +4988,6 @@ def recorded_contracts(request: NativeContractRequest) -> list[NativeContractOut return run, NativeTypeAnswers( normalized=normalized, typed_comparisons=contract_statuses["typed_comparisons"], - handbacks=contract_statuses["handbacks"], ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py index c3aa8d64a5..721cb03b5a 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_incremental_edits.py @@ -21,7 +21,6 @@ RandomEditChain, adapter_switched, add_external_flavor_macro, - analyze_in_one_batch, build_between, change_adapter_lexical_rules, compare_incremental_compile, @@ -411,9 +410,7 @@ def test_given_broken_cache_invalidation_when_compiling_an_edit_then_the_oracle_ @pytest.mark.parametrize( "test_case", - [ - SharedCacheKeyTestCase(description="one_batch", schedule=analyze_in_one_batch), - ], + [SharedCacheKeyTestCase(description="native_dependency_order")], ids=lambda case: case.description, ) def test_given_models_sharing_an_analysis_cache_key_when_upstream_changes_then_it_matches_uncached( @@ -424,7 +421,6 @@ def test_given_models_sharing_an_analysis_cache_key_when_upstream_changes_then_i ) -> None: project_dir: Path = compile_reuse_project enable_compile_reuse(monkeypatch) - test_case.schedule(monkeypatch) star_chain_with_twin_added(project_dir) assert compile_in_process(project_dir=project_dir) == 0 staging_extra_flag(project_dir) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py index f1600fafb9..cab39a8bab 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_type_system.py @@ -170,7 +170,7 @@ def test_given_contract_types_when_compiling_with_each_engine_then_reports_are_e assert report_without_engine(preview_run) == report_without_engine(native_run) assert preview_run.compiled == native_run.compiled assert native_answers == preview_answers - assert (native_answers.typed_comparisons >= 1, native_answers.handbacks) == (True, 0) + assert native_answers.typed_comparisons >= 1 if __name__ == "__main__":