kiri-cli 1.0.0b3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kiri-cli might be problematic. Click here for more details.
- kiri_adapters/__init__.py +193 -0
- kiri_adapters/backends/__init__.py +0 -0
- kiri_adapters/backends/databricks.manifest.yml +55 -0
- kiri_adapters/backends/databricks.py +2980 -0
- kiri_adapters/backends/fabric_dw.manifest.yml +17 -0
- kiri_adapters/backends/fabric_dw.py +307 -0
- kiri_adapters/backends/fabric_lakehouse.manifest.yml +19 -0
- kiri_adapters/backends/fabric_lakehouse.py +365 -0
- kiri_adapters/backends/iceberg.manifest.yml +17 -0
- kiri_adapters/backends/iceberg.py +451 -0
- kiri_adapters/backends/localduckdb.manifest.yml +43 -0
- kiri_adapters/backends/localduckdb.py +1295 -0
- kiri_adapters/backends/mssql.manifest.yml +17 -0
- kiri_adapters/backends/mssql.py +366 -0
- kiri_adapters/backends/postgres.manifest.yml +17 -0
- kiri_adapters/backends/postgres.py +434 -0
- kiri_adapters/backends/trino.manifest.yml +17 -0
- kiri_adapters/backends/trino.py +542 -0
- kiri_adapters/base.py +892 -0
- kiri_adapters/doctor_checks.py +81 -0
- kiri_adapters/dv_planner.py +1205 -0
- kiri_adapters/dv_planner_bridge.py +265 -0
- kiri_adapters/dv_planner_pit.py +265 -0
- kiri_adapters/dv_planner_ref.py +403 -0
- kiri_adapters/dv_planner_sat_variants.py +668 -0
- kiri_adapters/framework.py +112 -0
- kiri_adapters/incident/__init__.py +30 -0
- kiri_adapters/incident/_http.py +121 -0
- kiri_adapters/incident/base.py +89 -0
- kiri_adapters/incident/jira.py +365 -0
- kiri_adapters/incident/servicenow.py +314 -0
- kiri_adapters/incident/zendesk.py +285 -0
- kiri_adapters/manifest.py +466 -0
- kiri_adapters/orchestration/__init__.py +30 -0
- kiri_adapters/orchestration/airflow.py +117 -0
- kiri_adapters/orchestration/artifact_emitter.py +99 -0
- kiri_adapters/orchestration/autoloader_emitter.py +141 -0
- kiri_adapters/orchestration/azure_data_factory.py +137 -0
- kiri_adapters/orchestration/base.py +626 -0
- kiri_adapters/orchestration/databricks_workflows.py +1768 -0
- kiri_adapters/orchestration/k8s_cron.py +555 -0
- kiri_adapters/orchestration/lifecycle.py +222 -0
- kiri_adapters/orchestration/pipeline_compat.py +145 -0
- kiri_adapters/orchestration/runtime_python.py +188 -0
- kiri_adapters/paths.py +38 -0
- kiri_adapters/protocols/__init__.py +64 -0
- kiri_adapters/protocols/ai_service/__init__.py +7 -0
- kiri_adapters/protocols/ai_service/conformance.py +120 -0
- kiri_adapters/protocols/base.py +220 -0
- kiri_adapters/protocols/catalog/__init__.py +7 -0
- kiri_adapters/protocols/catalog/conformance.py +138 -0
- kiri_adapters/protocols/framework/__init__.py +7 -0
- kiri_adapters/protocols/framework/conformance.py +98 -0
- kiri_adapters/protocols/incident/__init__.py +7 -0
- kiri_adapters/protocols/incident/conformance.py +64 -0
- kiri_adapters/protocols/orchestration/__init__.py +7 -0
- kiri_adapters/protocols/orchestration/conformance.py +84 -0
- kiri_adapters/protocols/platform/__init__.py +7 -0
- kiri_adapters/protocols/platform/conformance.py +120 -0
- kiri_adapters/protocols/sql_dialect/__init__.py +7 -0
- kiri_adapters/protocols/sql_dialect/conformance.py +91 -0
- kiri_adapters/protocols/vault/__init__.py +7 -0
- kiri_adapters/protocols/vault/conformance.py +65 -0
- kiri_adapters/py.typed +0 -0
- kiri_adapters/registry.py +104 -0
- kiri_adapters/semantic/__init__.py +60 -0
- kiri_adapters/semantic/apply.py +274 -0
- kiri_adapters/semantic/base.py +530 -0
- kiri_adapters/semantic/cube.py +280 -0
- kiri_adapters/semantic/dbt_semantic.py +92 -0
- kiri_adapters/semantic/metric_views.py +249 -0
- kiri_adapters/semantic/metricflow.py +345 -0
- kiri_adapters/semantic/snowflake_views.py +75 -0
- kiri_adapters/sql_dialect.py +1592 -0
- kiri_adapters/tsql_to_spark.py +653 -0
- kiri_ai_gateway/__init__.py +147 -0
- kiri_ai_gateway/audit.py +334 -0
- kiri_ai_gateway/batch.py +293 -0
- kiri_ai_gateway/config.py +165 -0
- kiri_ai_gateway/gateway.py +642 -0
- kiri_ai_gateway/placeholder_guard.py +145 -0
- kiri_ai_gateway/prices/__init__.py +77 -0
- kiri_ai_gateway/prices/prices.yaml +64 -0
- kiri_ai_gateway/prompts/__init__.py +54 -0
- kiri_ai_gateway/prompts/airbyte_source_setup.yaml +212 -0
- kiri_ai_gateway/prompts/bdv_translation.yaml +90 -0
- kiri_ai_gateway/prompts/contract_draft_mvp.yaml +117 -0
- kiri_ai_gateway/prompts/describe_table.yaml +19 -0
- kiri_ai_gateway/prompts/design_gold_from_question.yaml +130 -0
- kiri_ai_gateway/prompts/detect_pii.yaml +59 -0
- kiri_ai_gateway/prompts/discover_classify_column.yaml +69 -0
- kiri_ai_gateway/prompts/discover_describe_table.yaml +61 -0
- kiri_ai_gateway/prompts/dv_quality_summary.yaml +36 -0
- kiri_ai_gateway/prompts/incident_assist.yaml +58 -0
- kiri_ai_gateway/prompts/infer_source_schema.yaml +31 -0
- kiri_ai_gateway/prompts/ingest_backend_chooser.yaml +116 -0
- kiri_ai_gateway/prompts/interactive_source_edit.yaml +94 -0
- kiri_ai_gateway/prompts/map_needed_entity_to_source.yaml +72 -0
- kiri_ai_gateway/prompts/meet_kiri.yaml +127 -0
- kiri_ai_gateway/prompts/native_source_setup.yaml +211 -0
- kiri_ai_gateway/prompts/source_grouping_reason.yaml +29 -0
- kiri_ai_gateway/prompts/suggest_attribute_classification.yaml +42 -0
- kiri_ai_gateway/prompts/suggest_data_tests.yaml +71 -0
- kiri_ai_gateway/prompts/suggest_gold_dimension.yaml +108 -0
- kiri_ai_gateway/prompts/suggest_gold_fact.yaml +107 -0
- kiri_ai_gateway/prompts/suggest_gold_model.yaml +70 -0
- kiri_ai_gateway/prompts/suggest_semantic_intent.yaml +91 -0
- kiri_ai_gateway/prompts/suggest_silver_model.yaml +140 -0
- kiri_ai_gateway/providers/__init__.py +7 -0
- kiri_ai_gateway/providers/anthropic_provider.py +26 -0
- kiri_ai_gateway/providers/gateway_provider.py +188 -0
- kiri_ai_gateway/providers/local_provider.py +177 -0
- kiri_ai_gateway/py.typed +0 -0
- kiri_ai_gateway/service.py +283 -0
- kiri_ai_gateway/transport.py +103 -0
- kiri_ai_gateway/transports/__init__.py +12 -0
- kiri_ai_gateway/transports/anthropic_transport.py +73 -0
- kiri_ai_gateway/transports/ollama_transport.py +82 -0
- kiri_ai_gateway/transports/openai_compatible_transport.py +125 -0
- kiri_ai_gateway/workspace_policy.py +52 -0
- kiri_catalog/__init__.py +199 -0
- kiri_catalog/annotation_plan.py +183 -0
- kiri_catalog/appearances.py +155 -0
- kiri_catalog/backends/__init__.py +79 -0
- kiri_catalog/backends/base.py +167 -0
- kiri_catalog/backends/memory.py +241 -0
- kiri_catalog/backends/postgres.py +497 -0
- kiri_catalog/backends/redacting.py +195 -0
- kiri_catalog/backends/sqlite.py +480 -0
- kiri_catalog/context.py +94 -0
- kiri_catalog/federation/__init__.py +114 -0
- kiri_catalog/federation/api.py +200 -0
- kiri_catalog/federation/cache.py +149 -0
- kiri_catalog/federation/emit.py +209 -0
- kiri_catalog/federation/fs_resolver.py +295 -0
- kiri_catalog/federation/http_resolver.py +486 -0
- kiri_catalog/federation/metrics.py +123 -0
- kiri_catalog/federation/release.py +342 -0
- kiri_catalog/federation/resolver.py +332 -0
- kiri_catalog/federation/schema.py +460 -0
- kiri_catalog/importer.py +668 -0
- kiri_catalog/lineage_drift.py +280 -0
- kiri_catalog/lineage_graph.py +176 -0
- kiri_catalog/lineage_graph_explain.py +96 -0
- kiri_catalog/lineage_graph_mermaid.py +230 -0
- kiri_catalog/observed_uc_lineage.py +216 -0
- kiri_catalog/pii_detect.py +176 -0
- kiri_catalog/push.py +122 -0
- kiri_catalog/py.typed +0 -0
- kiri_catalog/redact.py +101 -0
- kiri_catalog/review_actions.py +119 -0
- kiri_catalog/schema/__init__.py +70 -0
- kiri_catalog/schema/asset.py +142 -0
- kiri_catalog/schema/column.py +247 -0
- kiri_catalog/schema/enums.py +195 -0
- kiri_catalog/schema/job.py +51 -0
- kiri_catalog/schema/lineage.py +33 -0
- kiri_catalog/schema/policy.py +69 -0
- kiri_catalog/schema/primitives.py +90 -0
- kiri_catalog/schema/review_state.py +111 -0
- kiri_catalog/schema/urn.py +45 -0
- kiri_catalog/schema_discovery.py +160 -0
- kiri_catalog/sinks/__init__.py +64 -0
- kiri_catalog/sinks/base.py +178 -0
- kiri_catalog/sinks/horizon.py +195 -0
- kiri_catalog/sinks/purview.py +216 -0
- kiri_catalog/sinks/unity.py +352 -0
- kiri_catalog/sinks/unity_executor.py +543 -0
- kiri_catalog/sources/__init__.py +42 -0
- kiri_catalog/sources/base.py +104 -0
- kiri_catalog/sources/unity.py +465 -0
- kiri_catalog/steward_writeback.py +158 -0
- kiri_catalog/tag_sql.py +99 -0
- kiri_cli/LICENSE +105 -0
- kiri_cli/__init__.py +1 -0
- kiri_cli/_apply_cli.py +63 -0
- kiri_cli/_apply_reporter.py +38 -0
- kiri_cli/_closed_menu.py +36 -0
- kiri_cli/_fixture_option.py +18 -0
- kiri_cli/_layer_detect.py +158 -0
- kiri_cli/_project_option.py +79 -0
- kiri_cli/adapter_cmd.py +192 -0
- kiri_cli/ai_batch_cmd.py +258 -0
- kiri_cli/ai_cmd.py +352 -0
- kiri_cli/approve_cmd.py +659 -0
- kiri_cli/attr_cmd.py +575 -0
- kiri_cli/audit_cmd.py +348 -0
- kiri_cli/audit_correlate_cmd.py +648 -0
- kiri_cli/audit_join.py +437 -0
- kiri_cli/audit_join_cmd.py +160 -0
- kiri_cli/auto_cmd.py +97 -0
- kiri_cli/azure_cmd.py +243 -0
- kiri_cli/catalog.py +1293 -0
- kiri_cli/cli_reference_live.py +147 -0
- kiri_cli/compile_cmd.py +493 -0
- kiri_cli/compliance_cmd.py +221 -0
- kiri_cli/contract_approval_cmd.py +94 -0
- kiri_cli/contract_cmd.py +64 -0
- kiri_cli/contract_codeowners_cmd.py +162 -0
- kiri_cli/contract_diff.py +111 -0
- kiri_cli/contract_emit.py +90 -0
- kiri_cli/contract_export.py +192 -0
- kiri_cli/contract_lint.py +1505 -0
- kiri_cli/contract_merge_cmd.py +264 -0
- kiri_cli/contract_new.py +326 -0
- kiri_cli/contract_promote.py +216 -0
- kiri_cli/contract_scaffold_cmd.py +134 -0
- kiri_cli/contract_schema_export.py +69 -0
- kiri_cli/contract_show.py +150 -0
- kiri_cli/contract_stubs.py +60 -0
- kiri_cli/contract_sync_from_live.py +327 -0
- kiri_cli/contract_validate.py +154 -0
- kiri_cli/contract_verify_live.py +242 -0
- kiri_cli/cost_cmd.py +243 -0
- kiri_cli/databricks_health.py +1254 -0
- kiri_cli/databricks_setup_cmd.py +712 -0
- kiri_cli/discover_cmd.py +600 -0
- kiri_cli/docs_cmd.py +35 -0
- kiri_cli/doctor_cmd.py +145 -0
- kiri_cli/dv_cmd.py +814 -0
- kiri_cli/dv_conformance_cmd.py +154 -0
- kiri_cli/dv_install.py +493 -0
- kiri_cli/dv_migrate_cmd.py +183 -0
- kiri_cli/dv_test_cmd.py +175 -0
- kiri_cli/federation_cmd.py +315 -0
- kiri_cli/fk_cmd.py +160 -0
- kiri_cli/flows_cmd.py +910 -0
- kiri_cli/governance_cmd.py +358 -0
- kiri_cli/incident_api_cmd.py +180 -0
- kiri_cli/incident_cmd.py +410 -0
- kiri_cli/incident_setup_cmd.py +239 -0
- kiri_cli/ingest.py +427 -0
- kiri_cli/ingest_discover.py +480 -0
- kiri_cli/ingest_new_source.py +386 -0
- kiri_cli/intent_cmd.py +122 -0
- kiri_cli/invariants_cmd.py +118 -0
- kiri_cli/kiri_banner.py +161 -0
- kiri_cli/kpi_cmd.py +222 -0
- kiri_cli/layer_cmd.py +464 -0
- kiri_cli/lineage.py +601 -0
- kiri_cli/main.py +2472 -0
- kiri_cli/maturity_cmd.py +222 -0
- kiri_cli/migrate_cmd.py +1347 -0
- kiri_cli/onboard_cmd.py +223 -0
- kiri_cli/pack_cmd.py +496 -0
- kiri_cli/pipeline_cmd.py +127 -0
- kiri_cli/pre_classify_cmd.py +274 -0
- kiri_cli/project_cmd.py +592 -0
- kiri_cli/py.typed +0 -0
- kiri_cli/quality_cmd.py +295 -0
- kiri_cli/rbac.py +139 -0
- kiri_cli/rbac_cmd.py +291 -0
- kiri_cli/release.py +850 -0
- kiri_cli/release_semver.py +265 -0
- kiri_cli/repo_cmd.py +155 -0
- kiri_cli/retention_cmd.py +333 -0
- kiri_cli/sample_data/__init__.py +12 -0
- kiri_cli/sample_data/customers.contract.yml +47 -0
- kiri_cli/sample_data/customers.csv +25 -0
- kiri_cli/scaffold_cmd.py +721 -0
- kiri_cli/schema_cmd.py +185 -0
- kiri_cli/semantic_cmd.py +605 -0
- kiri_cli/silver_cmd.py +348 -0
- kiri_cli/skills_cmd.py +243 -0
- kiri_cli/skills_data/README.md +51 -0
- kiri_cli/skills_data/add-source/SKILL.md +116 -0
- kiri_cli/skills_data/azure-deploy/SKILL.md +261 -0
- kiri_cli/skills_data/azure-status/SKILL.md +111 -0
- kiri_cli/skills_data/compliance-check/SKILL.md +99 -0
- kiri_cli/skills_data/databricks-build-pipeline/SKILL.md +112 -0
- kiri_cli/skills_data/databricks-project-setup/SKILL.md +157 -0
- kiri_cli/skills_data/debug-apply/SKILL.md +186 -0
- kiri_cli/skills_data/draft-gold-model/SKILL.md +203 -0
- kiri_cli/skills_data/explain-kpi/SKILL.md +158 -0
- kiri_cli/skills_data/incident-coach/SKILL.md +135 -0
- kiri_cli/skills_data/incident-setup/SKILL.md +112 -0
- kiri_cli/skills_data/maturity-coach/SKILL.md +120 -0
- kiri_cli/skills_data/promote-contract/SKILL.md +91 -0
- kiri_cli/skills_data/review-attributes/SKILL.md +94 -0
- kiri_cli/sla_cmd.py +769 -0
- kiri_cli/source_cmd.py +909 -0
- kiri_cli/templates/__init__.py +6 -0
- kiri_cli/templates/dv4dbt_packages.yml.j2 +11 -0
- kiri_cli/upgrade_cmd.py +896 -0
- kiri_cli/use_cmd.py +120 -0
- kiri_cli/vault_cmd.py +241 -0
- kiri_cli/verify_generated.py +241 -0
- kiri_cli-1.0.0b3.dist-info/METADATA +212 -0
- kiri_cli-1.0.0b3.dist-info/RECORD +743 -0
- kiri_cli-1.0.0b3.dist-info/WHEEL +4 -0
- kiri_cli-1.0.0b3.dist-info/entry_points.txt +4 -0
- kiri_compliance/__init__.py +33 -0
- kiri_compliance/audit_coverage.py +354 -0
- kiri_compliance/controls.py +763 -0
- kiri_compliance/evidence.py +155 -0
- kiri_compliance/py.typed +0 -0
- kiri_compliance/quality_engine.py +246 -0
- kiri_compliance/renderer.py +195 -0
- kiri_compliance/report.py +44 -0
- kiri_compliance/schema.py +129 -0
- kiri_core/__init__.py +185 -0
- kiri_core/adapter_surfaces.py +149 -0
- kiri_core/ai_query_gate/__init__.py +92 -0
- kiri_core/ai_query_gate/audit.py +153 -0
- kiri_core/ai_query_gate/evaluator.py +484 -0
- kiri_core/ai_query_gate/models.py +162 -0
- kiri_core/ai_query_gate/parser.py +436 -0
- kiri_core/ai_query_gate/resolver.py +274 -0
- kiri_core/ai_query_gate/store.py +234 -0
- kiri_core/ai_query_gate/store_postgres.py +409 -0
- kiri_core/api_keys.py +243 -0
- kiri_core/apply/__init__.py +27 -0
- kiri_core/apply/conformance.py +99 -0
- kiri_core/apply/incident_router.py +176 -0
- kiri_core/apply/models.py +140 -0
- kiri_core/apply/reporter.py +46 -0
- kiri_core/apply/runner.py +3083 -0
- kiri_core/apply_log_store.py +155 -0
- kiri_core/approval_flow/__init__.py +133 -0
- kiri_core/approval_flow/integration.py +156 -0
- kiri_core/approval_flow/models.py +443 -0
- kiri_core/approval_flow/notifier.py +263 -0
- kiri_core/approval_flow/orchestrator.py +660 -0
- kiri_core/approval_flow/reconcile_signoff.py +53 -0
- kiri_core/approval_flow/store.py +220 -0
- kiri_core/approval_flow/store_jsonl.py +217 -0
- kiri_core/approval_flow/store_postgres.py +580 -0
- kiri_core/approval_policy.py +116 -0
- kiri_core/asgi_trace.py +160 -0
- kiri_core/compliance.py +802 -0
- kiri_core/cutover_approval.py +187 -0
- kiri_core/discovery/__init__.py +109 -0
- kiri_core/discovery/ai/__init__.py +37 -0
- kiri_core/discovery/ai/_payload.py +115 -0
- kiri_core/discovery/ai/budget.py +83 -0
- kiri_core/discovery/ai/classify.py +223 -0
- kiri_core/discovery/ai/describe.py +200 -0
- kiri_core/discovery/ai/errors.py +49 -0
- kiri_core/discovery/discover.py +648 -0
- kiri_core/discovery/heuristics/__init__.py +42 -0
- kiri_core/discovery/heuristics/classification.py +174 -0
- kiri_core/discovery/heuristics/medallion.py +157 -0
- kiri_core/discovery/heuristics/pii.py +92 -0
- kiri_core/discovery/introspector.py +636 -0
- kiri_core/discovery/introspector_databricks.py +396 -0
- kiri_core/discovery/introspector_postgres.py +394 -0
- kiri_core/discovery/introspector_snowflake.py +383 -0
- kiri_core/discovery/reporting/__init__.py +17 -0
- kiri_core/discovery/reporting/discovery_report.py +190 -0
- kiri_core/discovery/scaffold/__init__.py +41 -0
- kiri_core/discovery/scaffold/contract_writer.py +632 -0
- kiri_core/discovery/scaffold/provenance_overlay.py +143 -0
- kiri_core/discovery/scaffold/source_writer.py +211 -0
- kiri_core/discovery/scaffold/todo_marker.py +98 -0
- kiri_core/domain_policy.py +122 -0
- kiri_core/goal_coverage.py +82 -0
- kiri_core/gold_registry.py +211 -0
- kiri_core/health.py +192 -0
- kiri_core/incident.py +204 -0
- kiri_core/incident_builders.py +256 -0
- kiri_core/incident_log.py +418 -0
- kiri_core/intent/__init__.py +46 -0
- kiri_core/intent/agent_runner.py +82 -0
- kiri_core/intent/project_dv.py +261 -0
- kiri_core/intent/propose.py +132 -0
- kiri_core/intent/semantic_intent.py +446 -0
- kiri_core/invariants.py +301 -0
- kiri_core/kpi.py +373 -0
- kiri_core/latency.py +150 -0
- kiri_core/lifecycle/__init__.py +555 -0
- kiri_core/lifecycle/ai_index.py +453 -0
- kiri_core/lifecycle/auto_mode.py +172 -0
- kiri_core/lifecycle/bootstrap.py +316 -0
- kiri_core/lifecycle/cli_reference.py +1064 -0
- kiri_core/lifecycle/commit.py +258 -0
- kiri_core/lifecycle/compile_dispatch.py +296 -0
- kiri_core/lifecycle/compile_range.py +665 -0
- kiri_core/lifecycle/contract_inventory.py +354 -0
- kiri_core/lifecycle/contracts.py +250 -0
- kiri_core/lifecycle/doctor.py +412 -0
- kiri_core/lifecycle/git_remote.py +948 -0
- kiri_core/lifecycle/identity.py +147 -0
- kiri_core/lifecycle/ingestion_modes.py +215 -0
- kiri_core/lifecycle/layer_contract.py +452 -0
- kiri_core/lifecycle/layer_contract_migrate.py +969 -0
- kiri_core/lifecycle/layer_contract_parser.py +436 -0
- kiri_core/lifecycle/layer_contract_validator.py +2291 -0
- kiri_core/lifecycle/layout.py +298 -0
- kiri_core/lifecycle/onboarding.py +306 -0
- kiri_core/lifecycle/openlineage_emit.py +153 -0
- kiri_core/lifecycle/patch.py +231 -0
- kiri_core/lifecycle/plan.py +241 -0
- kiri_core/lifecycle/probe.py +204 -0
- kiri_core/lifecycle/project_context.py +334 -0
- kiri_core/lifecycle/project_status.py +532 -0
- kiri_core/lifecycle/scaffold.py +455 -0
- kiri_core/lifecycle/source_grouping.py +725 -0
- kiri_core/lifecycle/source_introspection.py +488 -0
- kiri_core/lifecycle/source_inventory.py +561 -0
- kiri_core/lifecycle/source_patch.py +353 -0
- kiri_core/lifecycle/sources.py +470 -0
- kiri_core/lifecycle/sync_from_live.py +727 -0
- kiri_core/lifecycle/validate.py +116 -0
- kiri_core/lifecycle/verify_annotations.py +391 -0
- kiri_core/lifecycle/verify_live.py +389 -0
- kiri_core/lifecycle/verify_metric_views.py +321 -0
- kiri_core/lifecycle/watermark.py +371 -0
- kiri_core/lifecycle/watermark_apply.py +656 -0
- kiri_core/lineage.py +418 -0
- kiri_core/loader.py +377 -0
- kiri_core/logging.py +374 -0
- kiri_core/maturity.py +592 -0
- kiri_core/migration_layer_policy.py +285 -0
- kiri_core/names.py +163 -0
- kiri_core/observed_lineage.py +62 -0
- kiri_core/odcs/__init__.py +15 -0
- kiri_core/otel_bootstrap.py +326 -0
- kiri_core/pipeline_ir.py +332 -0
- kiri_core/pipeline_loader.py +82 -0
- kiri_core/planning.py +110 -0
- kiri_core/project.py +1576 -0
- kiri_core/project_docs.py +199 -0
- kiri_core/py.typed +0 -0
- kiri_core/rate_limit.py +456 -0
- kiri_core/release_state/__init__.py +37 -0
- kiri_core/release_state/models.py +108 -0
- kiri_core/release_state/store.py +634 -0
- kiri_core/resilience/__init__.py +288 -0
- kiri_core/resilience/retry.py +173 -0
- kiri_core/runtime.py +40 -0
- kiri_core/schema/__init__.py +62 -0
- kiri_core/schema/column.py +140 -0
- kiri_core/schema/ingestion.py +665 -0
- kiri_core/schema/model.py +44 -0
- kiri_core/schema/reporting_goal.py +160 -0
- kiri_core/schema/source.py +188 -0
- kiri_core/silver/__init__.py +148 -0
- kiri_core/silver/apply_runner.py +830 -0
- kiri_core/silver/backfill.py +280 -0
- kiri_core/silver/dv_backend.py +426 -0
- kiri_core/silver/dv_graph.py +932 -0
- kiri_core/silver/dv_graph_mermaid.py +443 -0
- kiri_core/silver/dv_graph_payload.py +79 -0
- kiri_core/silver/dv_graph_views.py +416 -0
- kiri_core/silver/graph_projector.py +566 -0
- kiri_core/silver/hash_canonicaliser.py +172 -0
- kiri_core/silver/hash_policy.py +195 -0
- kiri_core/silver/load_ts_policy.py +202 -0
- kiri_core/silver/schema_evolution.py +410 -0
- kiri_core/silver/zone_rules.py +81 -0
- kiri_core/sizing.py +134 -0
- kiri_core/storage_paths.py +130 -0
- kiri_core/technique_validator.py +700 -0
- kiri_core/trace.py +409 -0
- kiri_core/types.py +41 -0
- kiri_core/urn.py +161 -0
- kiri_dbt_bridge/__init__.py +22 -0
- kiri_dbt_bridge/_schema_resolve.py +38 -0
- kiri_dbt_bridge/bridge.py +94 -0
- kiri_dbt_bridge/dbt.manifest.yml +26 -0
- kiri_dbt_bridge/dv4dbt/__init__.py +143 -0
- kiri_dbt_bridge/dv4dbt/backend.py +204 -0
- kiri_dbt_bridge/dv4dbt/bridge_generator.py +289 -0
- kiri_dbt_bridge/dv4dbt/compat.py +232 -0
- kiri_dbt_bridge/dv4dbt/hub_generator.py +282 -0
- kiri_dbt_bridge/dv4dbt/link_generator.py +306 -0
- kiri_dbt_bridge/dv4dbt/manifest.py +203 -0
- kiri_dbt_bridge/dv4dbt/pit_generator.py +318 -0
- kiri_dbt_bridge/dv4dbt/policy_translation.py +249 -0
- kiri_dbt_bridge/dv4dbt/project_writer.py +588 -0
- kiri_dbt_bridge/dv4dbt/ref_generator.py +404 -0
- kiri_dbt_bridge/dv4dbt/render.py +180 -0
- kiri_dbt_bridge/dv4dbt/sat_generator.py +480 -0
- kiri_dbt_bridge/dv4dbt/sat_variant_generator.py +529 -0
- kiri_dbt_bridge/dv4dbt/templates/__init__.py +16 -0
- kiri_dbt_bridge/dv4dbt/templates/bridge.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/dbt_project.yml.j2 +37 -0
- kiri_dbt_bridge/dv4dbt/templates/hub.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/link.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/packages.yml.j2 +10 -0
- kiri_dbt_bridge/dv4dbt/templates/pit.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/ref_historised.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/ref_non_historised.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat_effectivity.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat_multi_active.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat_non_historised.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat_record_tracking.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sat_virtual_end_dated.sql.j2 +9 -0
- kiri_dbt_bridge/dv4dbt/templates/sources.yml.j2 +39 -0
- kiri_dbt_bridge/framework_adapter.py +310 -0
- kiri_dbt_bridge/generate.py +372 -0
- kiri_dbt_bridge/gold/__init__.py +21 -0
- kiri_dbt_bridge/gold/_sql_emit.py +128 -0
- kiri_dbt_bridge/gold/assemble.py +90 -0
- kiri_dbt_bridge/gold/canonical_columns.py +168 -0
- kiri_dbt_bridge/gold/computed_expressions.py +179 -0
- kiri_dbt_bridge/gold/dialects/__init__.py +69 -0
- kiri_dbt_bridge/gold/dialects/databricks.py +150 -0
- kiri_dbt_bridge/gold/dialects/duckdb.py +120 -0
- kiri_dbt_bridge/gold/dialects/trino.py +136 -0
- kiri_dbt_bridge/gold/dim_generator.py +688 -0
- kiri_dbt_bridge/gold/dimension_refs.py +255 -0
- kiri_dbt_bridge/gold/dispatch.py +69 -0
- kiri_dbt_bridge/gold/fact_generator.py +534 -0
- kiri_dbt_bridge/gold/flat_generator.py +144 -0
- kiri_dbt_bridge/gold/lineage.py +484 -0
- kiri_dbt_bridge/gold/localized_twins.py +218 -0
- kiri_dbt_bridge/gold/measures.py +49 -0
- kiri_dbt_bridge/gold/role_playing.py +75 -0
- kiri_dbt_bridge/gold/surrogate_key.py +114 -0
- kiri_dbt_bridge/macros/kiri_scd1_dim.sql +57 -0
- kiri_dbt_bridge/macros/kiri_scd2_dim.sql +188 -0
- kiri_dbt_bridge/macros/kiri_sha256.sql +32 -0
- kiri_dbt_bridge/macros/kiri_string_cast.sql +37 -0
- kiri_dbt_bridge/macros/kiri_surrogate_key.sql +43 -0
- kiri_dbt_bridge/profiles.py +162 -0
- kiri_dbt_bridge/py.typed +0 -0
- kiri_dbt_bridge/safe_ident.py +44 -0
- kiri_dbt_bridge/silver_conformance.py +188 -0
- kiri_dbt_bridge/silver_kimball/__init__.py +27 -0
- kiri_dbt_bridge/silver_kimball/assemble.py +174 -0
- kiri_dbt_bridge/silver_kimball/dim_generator.py +602 -0
- kiri_dbt_bridge/silver_kimball/dispatch.py +86 -0
- kiri_dbt_bridge/silver_kimball/fact_generator.py +320 -0
- kiri_dbt_bridge/silver_kimball/flat_generator.py +156 -0
- kiri_dbt_bridge/silver_kimball/lineage.py +464 -0
- kiri_dbt_bridge/silver_kimball/watermark_window.py +199 -0
- kiri_dbt_bridge/silver_kimball/zone.py +68 -0
- kiri_dbt_bridge/silver_scd2_curated/__init__.py +46 -0
- kiri_dbt_bridge/silver_scd2_curated/assemble.py +213 -0
- kiri_dbt_bridge/silver_scd2_curated/curated_generator.py +351 -0
- kiri_dbt_bridge/silver_scd2_curated/standardized_generator.py +261 -0
- kiri_dbt_bridge/silver_scd2_curated/standardized_plan.py +254 -0
- kiri_iceberg/__init__.py +11 -0
- kiri_iceberg/catalog.py +128 -0
- kiri_iceberg/profile.py +131 -0
- kiri_iceberg/py.typed +0 -0
- kiri_ingestion/__init__.py +63 -0
- kiri_ingestion/airbyte_client.py +428 -0
- kiri_ingestion/database_fetcher.py +224 -0
- kiri_ingestion/layout.py +71 -0
- kiri_ingestion/mssql_introspect.py +565 -0
- kiri_ingestion/py.typed +0 -0
- kiri_ingestion/rest_fetcher.py +472 -0
- kiri_ingestion/runner.py +341 -0
- kiri_ingestion/schema_drift.py +222 -0
- kiri_ingestion/storage.py +621 -0
- kiri_mcp/__init__.py +5 -0
- kiri_mcp/audit.py +99 -0
- kiri_mcp/audit_decorators.py +252 -0
- kiri_mcp/main.py +22 -0
- kiri_mcp/py.typed +0 -0
- kiri_mcp/rbac.py +110 -0
- kiri_mcp/server.py +766 -0
- kiri_mcp/skills.py +297 -0
- kiri_mcp/tools/__init__.py +70 -0
- kiri_mcp/tools/_shared.py +58 -0
- kiri_mcp/tools/adapter.py +150 -0
- kiri_mcp/tools/cli_reference.py +59 -0
- kiri_mcp/tools/compliance.py +119 -0
- kiri_mcp/tools/contract.py +374 -0
- kiri_mcp/tools/dv.py +62 -0
- kiri_mcp/tools/federation.py +152 -0
- kiri_mcp/tools/intent.py +71 -0
- kiri_mcp/tools/lineage.py +204 -0
- kiri_mcp/tools/lineage_drift.py +75 -0
- kiri_mcp/tools/plan.py +66 -0
- kiri_mcp/tools/project.py +311 -0
- kiri_migration/__init__.py +56 -0
- kiri_migration/name_preservation.py +271 -0
- kiri_migration/predeploy.py +311 -0
- kiri_migration/py.typed +0 -0
- kiri_migration/reconcile.py +1891 -0
- kiri_migration/scaffold/__init__.py +90 -0
- kiri_migration/scaffold/_audit_columns.py +74 -0
- kiri_migration/scaffold/_classify.py +100 -0
- kiri_migration/scaffold/_typemap.py +71 -0
- kiri_migration/scaffold/_yaml.py +33 -0
- kiri_migration/scaffold/bronze.py +279 -0
- kiri_migration/scaffold/compile_targets/__init__.py +271 -0
- kiri_migration/scaffold/compile_targets/_common.py +193 -0
- kiri_migration/scaffold/compile_targets/copy_into.py +33 -0
- kiri_migration/scaffold/compile_targets/dlt.py +125 -0
- kiri_migration/scaffold/compile_targets/dlt_with_append_flows.py +163 -0
- kiri_migration/scaffold/compile_targets/dlt_with_apply_changes.py +192 -0
- kiri_migration/scaffold/compile_targets/dlt_with_expectations.py +276 -0
- kiri_migration/scaffold/compile_targets/layer_dispatch.py +258 -0
- kiri_migration/scaffold/compile_targets/sql_full_snapshot.py +385 -0
- kiri_migration/scaffold/compile_targets/streaming_table.py +37 -0
- kiri_migration/scaffold/mart.py +234 -0
- kiri_migration/scaffold/plans/__init__.py +49 -0
- kiri_migration/scaffold/plans/_registry.py +308 -0
- kiri_migration/scaffold/plans/minimal.py +550 -0
- kiri_migration/scaffold/plans/plan_b_layer.py +640 -0
- kiri_migration/scaffold/silver_from_marts.py +256 -0
- kiri_migration/scaffold/silver_pattern.py +451 -0
- kiri_migration/sp_translator.py +398 -0
- kiri_odcs/LICENSE +204 -0
- kiri_odcs/__init__.py +234 -0
- kiri_odcs/ai_trust_tier.py +475 -0
- kiri_odcs/canonical.py +263 -0
- kiri_odcs/diff.py +63 -0
- kiri_odcs/errors.py +51 -0
- kiri_odcs/invariant_sql.py +136 -0
- kiri_odcs/kiri_bridge.py +71 -0
- kiri_odcs/kiri_ext.py +3166 -0
- kiri_odcs/kiri_fk.py +447 -0
- kiri_odcs/kiri_pit.py +121 -0
- kiri_odcs/kiri_ref.py +57 -0
- kiri_odcs/openlineage_adapter.py +105 -0
- kiri_odcs/parser.py +1213 -0
- kiri_odcs/pii_guardrails.py +159 -0
- kiri_odcs/py.typed +0 -0
- kiri_odcs/schema_export.py +223 -0
- kiri_openlineage/__init__.py +58 -0
- kiri_openlineage/builder.py +146 -0
- kiri_openlineage/cost.py +190 -0
- kiri_openlineage/emitter.py +165 -0
- kiri_openlineage/py.typed +0 -0
- kiri_openlineage/schema.py +121 -0
- kiri_packs/__init__.py +67 -0
- kiri_packs/installer.py +500 -0
- kiri_packs/manifest.py +134 -0
- kiri_packs/publisher.py +605 -0
- kiri_packs/py.typed +0 -0
- kiri_packs/validator.py +295 -0
- kiri_quality/__init__.py +108 -0
- kiri_quality/ai_audit_correlator.py +293 -0
- kiri_quality/ai_audit_correlator_apply.py +526 -0
- kiri_quality/conformance_evidence.py +272 -0
- kiri_quality/dbt_test_collector.py +214 -0
- kiri_quality/dq_codegen.py +300 -0
- kiri_quality/dv_checks.py +393 -0
- kiri_quality/dv_column_lineage.py +279 -0
- kiri_quality/dv_column_lineage_apply.py +856 -0
- kiri_quality/dv_conformance.py +841 -0
- kiri_quality/dv_fixture_runner.py +478 -0
- kiri_quality/dv_invariants.py +986 -0
- kiri_quality/dv_kiri.py +97 -0
- kiri_quality/dv_markdown.py +169 -0
- kiri_quality/dv_observed_uc_reader.py +187 -0
- kiri_quality/dv_runner.py +389 -0
- kiri_quality/evidence_status.py +337 -0
- kiri_quality/fk_checks.py +800 -0
- kiri_quality/fk_ddl.py +402 -0
- kiri_quality/fk_pit_contract.py +168 -0
- kiri_quality/fk_runner.py +950 -0
- kiri_quality/kernel.py +376 -0
- kiri_quality/pii.py +60 -0
- kiri_quality/pre_classify/__init__.py +55 -0
- kiri_quality/pre_classify/engine.py +346 -0
- kiri_quality/pre_classify/locale_packs/en.yaml +117 -0
- kiri_quality/pre_classify/locale_packs/sv.yaml +73 -0
- kiri_quality/pre_classify/merge.py +297 -0
- kiri_quality/pre_classify/model.py +163 -0
- kiri_quality/py.typed +0 -0
- kiri_quality/reject_lane.py +261 -0
- kiri_quality/rule_runner.py +565 -0
- kiri_quality/schema_evolution_check.py +88 -0
- kiri_quality/silver_quality.py +494 -0
- kiri_quality/silver_quality_markdown.py +187 -0
- kiri_quality/uc_security_compile.py +951 -0
- kiri_quality/uc_security_compile_apply.py +1186 -0
- kiri_rbac/__init__.py +87 -0
- kiri_rbac/access.py +371 -0
- kiri_rbac/loader.py +104 -0
- kiri_rbac/pr_gate.py +213 -0
- kiri_rbac/providers/__init__.py +29 -0
- kiri_rbac/providers/_jwt.py +121 -0
- kiri_rbac/providers/azure_ad.py +93 -0
- kiri_rbac/providers/base.py +107 -0
- kiri_rbac/providers/factory.py +48 -0
- kiri_rbac/providers/github.py +134 -0
- kiri_rbac/providers/oidc_generic.py +99 -0
- kiri_rbac/py.typed +0 -0
- kiri_rbac/resolver.py +118 -0
- kiri_rbac/schema.py +219 -0
- kiri_schema_registry/__init__.py +22 -0
- kiri_schema_registry/avro.py +127 -0
- kiri_schema_registry/base.py +71 -0
- kiri_schema_registry/confluent.py +184 -0
- kiri_schema_registry/glue.py +178 -0
- kiri_schema_registry/py.typed +0 -0
- kiri_slack_bot/__init__.py +24 -0
- kiri_slack_bot/handler.py +248 -0
- kiri_slack_bot/py.typed +0 -0
- kiri_slack_bot/queries.py +90 -0
- kiri_slack_bot/signature.py +94 -0
- kiri_vault/__init__.py +59 -0
- kiri_vault/audit.py +64 -0
- kiri_vault/aws_vault.py +123 -0
- kiri_vault/azure_vault.py +236 -0
- kiri_vault/boundaries.py +219 -0
- kiri_vault/client.py +54 -0
- kiri_vault/databricks_vault.py +292 -0
- kiri_vault/env_vault.py +77 -0
- kiri_vault/py.typed +0 -0
- kiri_vault/ref.py +84 -0
- kirimana_automation/__init__.py +0 -0
- kirimana_automation/business_calendar.py +179 -0
- kirimana_automation/dag_snapshot.py +252 -0
- kirimana_automation/db/__init__.py +5 -0
- kirimana_automation/db/migrate.py +571 -0
- kirimana_automation/db/migrations/0001_release_state.down.duckdb.sql +29 -0
- kirimana_automation/db/migrations/0001_release_state.down.postgres.sql +37 -0
- kirimana_automation/db/migrations/0001_release_state.duckdb.sql +170 -0
- kirimana_automation/db/migrations/0001_release_state.postgres.sql +176 -0
- kirimana_automation/db/migrations/0002_rate_limit_buckets.down.duckdb.sql +9 -0
- kirimana_automation/db/migrations/0002_rate_limit_buckets.down.postgres.sql +9 -0
- kirimana_automation/db/migrations/0002_rate_limit_buckets.duckdb.sql +14 -0
- kirimana_automation/db/migrations/0002_rate_limit_buckets.postgres.sql +18 -0
- kirimana_automation/db/migrations/0003_flow_runs.down.duckdb.sql +18 -0
- kirimana_automation/db/migrations/0003_flow_runs.down.postgres.sql +19 -0
- kirimana_automation/db/migrations/0003_flow_runs.duckdb.sql +85 -0
- kirimana_automation/db/migrations/0003_flow_runs.postgres.sql +102 -0
- kirimana_automation/db/migrations/0004_flow_dag_snapshots.down.duckdb.sql +11 -0
- kirimana_automation/db/migrations/0004_flow_dag_snapshots.down.postgres.sql +11 -0
- kirimana_automation/db/migrations/0004_flow_dag_snapshots.duckdb.sql +15 -0
- kirimana_automation/db/migrations/0004_flow_dag_snapshots.postgres.sql +31 -0
- kirimana_automation/db/migrations/0005_calendar_skip.down.duckdb.sql +109 -0
- kirimana_automation/db/migrations/0005_calendar_skip.down.postgres.sql +42 -0
- kirimana_automation/db/migrations/0005_calendar_skip.duckdb.sql +113 -0
- kirimana_automation/db/migrations/0005_calendar_skip.postgres.sql +42 -0
- kirimana_automation/db/migrations/0006_flow_apply_log.down.duckdb.sql +9 -0
- kirimana_automation/db/migrations/0006_flow_apply_log.down.postgres.sql +11 -0
- kirimana_automation/db/migrations/0006_flow_apply_log.duckdb.sql +45 -0
- kirimana_automation/db/migrations/0006_flow_apply_log.postgres.sql +49 -0
- kirimana_automation/db/migrations/0007_flow_task_error_redacted.down.duckdb.sql +14 -0
- kirimana_automation/db/migrations/0007_flow_task_error_redacted.down.postgres.sql +2 -0
- kirimana_automation/db/migrations/0007_flow_task_error_redacted.duckdb.sql +7 -0
- kirimana_automation/db/migrations/0007_flow_task_error_redacted.postgres.sql +11 -0
- kirimana_automation/db/migrations/README.md +84 -0
- kirimana_automation/flow_loader.py +220 -0
- kirimana_automation/freeze.py +133 -0
- kirimana_automation/kiri_recommender.py +313 -0
- kirimana_automation/main.py +195 -0
- kirimana_automation/py.typed +0 -0
- kirimana_automation/run_history.py +593 -0
- kirimana_automation/run_history_analysis.py +607 -0
- kirimana_automation/schedule.py +343 -0
- kirimana_automation/schedule_validator.py +572 -0
- kirimana_automation/workflow_compiler.py +1288 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""Silver-Kimball FACT generator (ADR 0099).
|
|
2
|
+
|
|
3
|
+
Mirrors :mod:`kiri_dbt_bridge.gold.fact_generator` with two differences:
|
|
4
|
+
|
|
5
|
+
- reads ``contract.kiri.silver`` instead of ``contract.kiri.gold``;
|
|
6
|
+
- pulls FROM bronze via :func:`silver_kimball.lineage.from_clause`.
|
|
7
|
+
|
|
8
|
+
Silver-Kimball facts have **no** ``dimension_refs`` — at the
|
|
9
|
+
conformed-master layer the silver facts ARE the atomic facts, joining
|
|
10
|
+
silver dims by their PK directly. There is no separate dim-FK
|
|
11
|
+
validation step; the projection emits each silver column from its
|
|
12
|
+
own lineage and authors point FK columns at the right bronze PK.
|
|
13
|
+
|
|
14
|
+
Lineage-driven projection emits each silver column as
|
|
15
|
+
``<bronze_alias>.<bronze_col> AS <silver_col>``.
|
|
16
|
+
|
|
17
|
+
Every silver-fact column must declare a non-empty
|
|
18
|
+
``kiri.lineage.from_columns``. Empty-lineage on facts means we'd
|
|
19
|
+
silently drop the column from emission — fail closed.
|
|
20
|
+
|
|
21
|
+
Measure validation walks lineage to the bronze source and rejects
|
|
22
|
+
SUM / AVG over non-numeric bronze columns.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import TYPE_CHECKING
|
|
29
|
+
|
|
30
|
+
from kiri_odcs.kiri_ext import GoldKind, MeasureAggregation
|
|
31
|
+
|
|
32
|
+
from kiri_dbt_bridge.silver_kimball.lineage import (
|
|
33
|
+
SilverLineagePlan,
|
|
34
|
+
build_silver_lineage_plan,
|
|
35
|
+
from_clause,
|
|
36
|
+
parse_bronze_lineage,
|
|
37
|
+
render_property_projection,
|
|
38
|
+
)
|
|
39
|
+
from kiri_dbt_bridge.silver_kimball.watermark_window import (
|
|
40
|
+
render_pre_hook,
|
|
41
|
+
render_window_predicate,
|
|
42
|
+
)
|
|
43
|
+
from kiri_dbt_bridge.silver_kimball.zone import resolve_silver_schema
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from kiri_core import CanonicalContract, Project
|
|
47
|
+
from kiri_core.lifecycle import WatermarkWindow
|
|
48
|
+
|
|
49
|
+
from kiri_dbt_bridge.gold.dialects import GoldDialect
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
_NUMERIC_LOGICAL_TYPES = {"integer", "long", "number", "decimal", "float", "double"}
|
|
53
|
+
_NUMERIC_REQUIRED_AGGS = {MeasureAggregation.SUM, MeasureAggregation.AVG}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _check_all_columns_have_lineage(contract: CanonicalContract) -> None:
|
|
57
|
+
"""Raise if any silver-fact column has no or empty lineage."""
|
|
58
|
+
schema = contract.primary_schema
|
|
59
|
+
if schema is None:
|
|
60
|
+
raise ValueError(f"Silver-Kimball fact {contract.contract.name!r} has no schema.")
|
|
61
|
+
missing: list[str] = []
|
|
62
|
+
empty: list[str] = []
|
|
63
|
+
for prop in schema.properties:
|
|
64
|
+
key = f"{schema.name}.{prop.name}"
|
|
65
|
+
col_meta = contract.kiri.properties.get(key)
|
|
66
|
+
if col_meta is None or col_meta.lineage is None:
|
|
67
|
+
missing.append(prop.name)
|
|
68
|
+
continue
|
|
69
|
+
if not col_meta.lineage.from_columns:
|
|
70
|
+
empty.append(prop.name)
|
|
71
|
+
if missing:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"Silver-Kimball fact {contract.contract.name!r}: column(s) "
|
|
74
|
+
f"{missing} have no kiri.lineage.from_columns declared. Every "
|
|
75
|
+
"fact column must declare lineage; empty list ('originates "
|
|
76
|
+
"here') is only valid on dimensions."
|
|
77
|
+
)
|
|
78
|
+
if empty:
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"Silver-Kimball fact {contract.contract.name!r}: column(s) "
|
|
81
|
+
f"{empty} have empty kiri.lineage.from_columns. Facts have no "
|
|
82
|
+
"'originates here' columns — declare the bronze source for "
|
|
83
|
+
"every column or remove it from the schema."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _validate_measures(contract: CanonicalContract, project: Project) -> None:
|
|
88
|
+
"""Reject measures whose lineage-resolved bronze source type can't
|
|
89
|
+
sustain the declared aggregation.
|
|
90
|
+
"""
|
|
91
|
+
schema = contract.primary_schema
|
|
92
|
+
if schema is None:
|
|
93
|
+
return
|
|
94
|
+
bronze_by_name: dict[str, CanonicalContract] = {c.contract.name: c for c in project.contracts}
|
|
95
|
+
for measure in contract.kiri.silver.measures:
|
|
96
|
+
if measure.aggregation not in _NUMERIC_REQUIRED_AGGS:
|
|
97
|
+
continue
|
|
98
|
+
key = f"{schema.name}.{measure.column}"
|
|
99
|
+
col_meta = contract.kiri.properties.get(key)
|
|
100
|
+
if col_meta is None or col_meta.lineage is None or not col_meta.lineage.from_columns:
|
|
101
|
+
continue
|
|
102
|
+
first_urn = col_meta.lineage.from_columns[0]
|
|
103
|
+
try:
|
|
104
|
+
ref = parse_bronze_lineage(first_urn)
|
|
105
|
+
except ValueError:
|
|
106
|
+
continue
|
|
107
|
+
bronze = bronze_by_name.get(ref.contract_name)
|
|
108
|
+
if bronze is None or bronze.primary_schema is None:
|
|
109
|
+
continue
|
|
110
|
+
bronze_col_type: str | None = None
|
|
111
|
+
for prop in bronze.primary_schema.properties:
|
|
112
|
+
if prop.name == ref.column:
|
|
113
|
+
bronze_col_type = (prop.logical_type or "").lower()
|
|
114
|
+
break
|
|
115
|
+
if bronze_col_type and bronze_col_type not in _NUMERIC_LOGICAL_TYPES:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"Silver-Kimball fact {contract.contract.name!r}: measure "
|
|
118
|
+
f"{measure.name!r} declares aggregation "
|
|
119
|
+
f"{measure.aggregation.value!r} over silver column "
|
|
120
|
+
f"{measure.column!r}, whose lineage resolves to bronze "
|
|
121
|
+
f"{ref.contract_name}::{ref.column} with "
|
|
122
|
+
f"logicalType={bronze_col_type!r}. "
|
|
123
|
+
f"{measure.aggregation.value.upper()} requires a numeric "
|
|
124
|
+
f"bronze source ({sorted(_NUMERIC_LOGICAL_TYPES)}). "
|
|
125
|
+
"Either change the aggregation (e.g. COUNT) or "
|
|
126
|
+
"pre-compute a numeric column in bronze."
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def generate_fact_model(
|
|
131
|
+
contract: CanonicalContract,
|
|
132
|
+
project: Project,
|
|
133
|
+
dialect: GoldDialect,
|
|
134
|
+
window: WatermarkWindow | None = None,
|
|
135
|
+
) -> str:
|
|
136
|
+
"""Render the full ``models/silver/<fact_name>.sql`` body.
|
|
137
|
+
|
|
138
|
+
Backwards compatible (ADR 0080 "backwards compat"): ``window=None`` — or a
|
|
139
|
+
contract that does not declare ``kiri.watermark`` — yields the byte-identical
|
|
140
|
+
``materialized='table'`` full-build SQL this generator always produced. When a
|
|
141
|
+
``window`` is supplied AND the contract declares ``kiri.watermark``, the model
|
|
142
|
+
becomes ``materialized='incremental'`` with ``incremental_strategy='append'``
|
|
143
|
+
(a fact is an append of new fact rows) paired with a ``pre_hook`` window-delete
|
|
144
|
+
— EXACTLY the flat shape (ADR 0080 §3/§4 Slice D). A fact carries no dbt
|
|
145
|
+
``unique_key`` (its grain is not a merge key), and in the installed
|
|
146
|
+
dbt-duckdb/dbt-databricks macros a keyless ``delete+insert`` does NOT delete,
|
|
147
|
+
so the keyless ``append`` + a ``pre_hook`` that clears exactly the
|
|
148
|
+
``(lower, upper]`` window before the append is the portable idempotent path
|
|
149
|
+
(see :func:`kiri_dbt_bridge.silver_kimball.watermark_window.render_pre_hook`).
|
|
150
|
+
|
|
151
|
+
Load-bearing (ADR 0080 §4): you must NOT add a bare source-window WHERE to the
|
|
152
|
+
current full-rebuild ``materialized='table'`` model — that would replace the
|
|
153
|
+
table with only the windowed rows (data loss). The model becomes genuinely
|
|
154
|
+
incremental; the source WHERE is guarded by ``{% if is_incremental() %}`` so
|
|
155
|
+
the first run builds full up to ``upper``, and the post-success advance reads
|
|
156
|
+
``max(<field>)`` over the materialised SILVER OUTPUT — so the watermark field
|
|
157
|
+
MUST be a projected output column (:func:`render_window_predicate` fails closed
|
|
158
|
+
when it is not).
|
|
159
|
+
"""
|
|
160
|
+
silver = contract.kiri.silver
|
|
161
|
+
if silver.kind not in (
|
|
162
|
+
GoldKind.FACT,
|
|
163
|
+
GoldKind.FACTLESS_FACT,
|
|
164
|
+
GoldKind.SNAPSHOT_FACT,
|
|
165
|
+
GoldKind.ACCUMULATING_SNAPSHOT,
|
|
166
|
+
):
|
|
167
|
+
raise ValueError(
|
|
168
|
+
f"generate_fact_model called with kind={silver.kind!r}; "
|
|
169
|
+
"this generator handles the four FACT-family kinds: "
|
|
170
|
+
"fact / factless_fact / snapshot_fact / accumulating_snapshot."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if not silver.grain:
|
|
174
|
+
raise ValueError(
|
|
175
|
+
f"Silver-Kimball fact {contract.contract.name!r} has no grain — "
|
|
176
|
+
"set kiri.silver.grain to the column(s) that identify a row."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
coarser_grain = len(silver.grain) > 1
|
|
180
|
+
|
|
181
|
+
_check_all_columns_have_lineage(contract)
|
|
182
|
+
_validate_measures(contract, project)
|
|
183
|
+
|
|
184
|
+
plan = build_silver_lineage_plan(contract, project)
|
|
185
|
+
schema = resolve_silver_schema(contract, project)
|
|
186
|
+
|
|
187
|
+
# Incremental ONLY when a window is supplied AND the contract declares a
|
|
188
|
+
# watermark. Otherwise the byte-identical full-table build (backwards compat).
|
|
189
|
+
incremental = window is not None and contract.kiri.watermark is not None
|
|
190
|
+
|
|
191
|
+
if incremental:
|
|
192
|
+
assert window is not None # narrowed by `incremental`
|
|
193
|
+
pre_hook = render_pre_hook(contract, window, dialect)
|
|
194
|
+
config = (
|
|
195
|
+
f"{{{{ config(materialized='incremental', incremental_strategy='append'"
|
|
196
|
+
f"{pre_hook}, "
|
|
197
|
+
f"schema='{schema}') }}}}\n"
|
|
198
|
+
f"-- Generated from silver-Kimball contract {contract.contract.name!r} "
|
|
199
|
+
f"({silver.kind.value}, grain={silver.grain}, incremental over a "
|
|
200
|
+
f"store-derived watermark window, ADR 0080 §3/§4). append + pre_hook "
|
|
201
|
+
f"window-delete (idempotent re-run: the pre_hook deletes exactly the "
|
|
202
|
+
f"(lower, upper] window from this before the append — no duplicate "
|
|
203
|
+
f"facts; a keyless delete+insert would NOT delete). Target-native SQL "
|
|
204
|
+
f"emitted via the {dialect.name!r} dialect helper (ADR 0099).\n"
|
|
205
|
+
)
|
|
206
|
+
predicate = render_window_predicate(contract, window, dialect, plan, shape="fact")
|
|
207
|
+
else:
|
|
208
|
+
config = (
|
|
209
|
+
f"{{{{ config(materialized='table', schema='{schema}') }}}}\n"
|
|
210
|
+
f"-- Generated from silver-Kimball contract {contract.contract.name!r} "
|
|
211
|
+
f"({silver.kind.value}, grain={silver.grain}). Target-native SQL "
|
|
212
|
+
f"emitted via the {dialect.name!r} dialect helper (ADR 0099).\n"
|
|
213
|
+
)
|
|
214
|
+
predicate = ""
|
|
215
|
+
|
|
216
|
+
body = (
|
|
217
|
+
_coarser_grain_sql(contract, plan, predicate)
|
|
218
|
+
if coarser_grain
|
|
219
|
+
else _row_grain_sql(contract, plan, predicate)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return config + "\n" + body
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _row_grain_sql(contract: CanonicalContract, plan: SilverLineagePlan, predicate: str) -> str:
|
|
226
|
+
"""Row-grain fact: plain SELECT projection + FROM/JOIN [+ window WHERE]."""
|
|
227
|
+
projection_lines = [
|
|
228
|
+
render_property_projection(contract, silver_col, ref, plan)
|
|
229
|
+
for silver_col, ref in plan.column_sources
|
|
230
|
+
]
|
|
231
|
+
if not projection_lines:
|
|
232
|
+
raise ValueError("Row-grain silver fact produced no projection lines.")
|
|
233
|
+
body = "select\n " + ",\n ".join(projection_lines) + "\n" + from_clause(plan) + "\n"
|
|
234
|
+
if predicate:
|
|
235
|
+
body += predicate + "\n"
|
|
236
|
+
return body
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _coarser_grain_sql(contract: CanonicalContract, plan: SilverLineagePlan, predicate: str) -> str:
|
|
240
|
+
"""Coarser-grain fact: GROUP BY the grain columns + aggregate measures.
|
|
241
|
+
|
|
242
|
+
Each grain column is projected as ``<alias>.<bronze_col> AS <grain_col>``;
|
|
243
|
+
each declared ``kiri.silver.measures`` entry emits
|
|
244
|
+
``<AGG>(<alias>.<bronze_col>) AS <measure_name>``; every OTHER
|
|
245
|
+
silver column (degenerate dimensions, FK columns reached via the
|
|
246
|
+
grain) is projected raw — they must be functionally determined by
|
|
247
|
+
the grain (Kimball invariant).
|
|
248
|
+
"""
|
|
249
|
+
silver = contract.kiri.silver
|
|
250
|
+
grain_set = set(silver.grain)
|
|
251
|
+
measure_source_cols = {m.column for m in silver.measures}
|
|
252
|
+
|
|
253
|
+
sources: dict[str, object] = dict(plan.column_sources)
|
|
254
|
+
aliases = plan.aliases
|
|
255
|
+
|
|
256
|
+
select_lines: list[str] = []
|
|
257
|
+
group_by_lines: list[str] = []
|
|
258
|
+
|
|
259
|
+
for grain_col in silver.grain:
|
|
260
|
+
ref = sources.get(grain_col)
|
|
261
|
+
if ref is None:
|
|
262
|
+
raise ValueError(
|
|
263
|
+
f"Silver-Kimball fact {contract.contract.name!r}: grain "
|
|
264
|
+
f"column {grain_col!r} has no lineage source."
|
|
265
|
+
)
|
|
266
|
+
qualified = f"{aliases[ref.contract_name]}.{ref.column}" # type: ignore[attr-defined]
|
|
267
|
+
select_lines.append(f"{qualified} as {grain_col}")
|
|
268
|
+
group_by_lines.append(qualified)
|
|
269
|
+
|
|
270
|
+
for measure in silver.measures:
|
|
271
|
+
m_ref = sources.get(measure.column)
|
|
272
|
+
if m_ref is None:
|
|
273
|
+
raise ValueError(
|
|
274
|
+
f"Silver-Kimball fact {contract.contract.name!r}: measure "
|
|
275
|
+
f"{measure.name!r} references column "
|
|
276
|
+
f"{measure.column!r} which has no lineage source."
|
|
277
|
+
)
|
|
278
|
+
bronze_alias = aliases[m_ref.contract_name] # type: ignore[attr-defined]
|
|
279
|
+
bronze_col = m_ref.column # type: ignore[attr-defined]
|
|
280
|
+
operand = f"{bronze_alias}.{bronze_col}"
|
|
281
|
+
agg = measure.aggregation.value.upper()
|
|
282
|
+
expr = f"COUNT(DISTINCT {operand})" if agg == "COUNT_DISTINCT" else f"{agg}({operand})"
|
|
283
|
+
select_lines.append(f"{expr} as {measure.name}")
|
|
284
|
+
|
|
285
|
+
for silver_col, ref in plan.column_sources:
|
|
286
|
+
if silver_col in grain_set or silver_col in measure_source_cols:
|
|
287
|
+
continue
|
|
288
|
+
qualified_ref = f"{aliases[ref.contract_name]}.{ref.column}"
|
|
289
|
+
select_lines.append(f"{qualified_ref} as {silver_col}")
|
|
290
|
+
group_by_lines.append(qualified_ref)
|
|
291
|
+
|
|
292
|
+
# The store-derived window WHERE (guarded by {% if is_incremental() %})
|
|
293
|
+
# filters the bronze SOURCE rows BEFORE the aggregation, so it slots between
|
|
294
|
+
# FROM and GROUP BY. Empty (no window / non-incremental) → unchanged SQL.
|
|
295
|
+
where = f"{predicate}\n" if predicate else ""
|
|
296
|
+
return (
|
|
297
|
+
"select\n "
|
|
298
|
+
+ ",\n ".join(select_lines)
|
|
299
|
+
+ "\n"
|
|
300
|
+
+ from_clause(plan)
|
|
301
|
+
+ "\n"
|
|
302
|
+
+ where
|
|
303
|
+
+ "group by "
|
|
304
|
+
+ ", ".join(group_by_lines)
|
|
305
|
+
+ "\n"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def write_fact_model(
|
|
310
|
+
contract: CanonicalContract,
|
|
311
|
+
project: Project,
|
|
312
|
+
dialect: GoldDialect,
|
|
313
|
+
output_dir: Path,
|
|
314
|
+
window: WatermarkWindow | None = None,
|
|
315
|
+
) -> Path:
|
|
316
|
+
"""Write the model file and return its path."""
|
|
317
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
318
|
+
path = output_dir / f"{contract.contract.name}.sql"
|
|
319
|
+
path.write_text(generate_fact_model(contract, project, dialect, window), encoding="utf-8")
|
|
320
|
+
return path
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Silver-Kimball FLAT generator (ADR 0099).
|
|
2
|
+
|
|
3
|
+
Explicit opt-out from Kimball star-schema modelling
|
|
4
|
+
(``kiri.silver.kind=flat``). The silver contract just projects columns
|
|
5
|
+
from one or more bronze sources — no grain, no business keys, no
|
|
6
|
+
SCD semantics. lint's dimensional rules skip flat contracts.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from kiri_odcs.kiri_ext import GoldKind
|
|
15
|
+
|
|
16
|
+
from kiri_dbt_bridge.silver_kimball.lineage import (
|
|
17
|
+
build_silver_lineage_plan,
|
|
18
|
+
from_clause,
|
|
19
|
+
render_property_projection,
|
|
20
|
+
)
|
|
21
|
+
from kiri_dbt_bridge.silver_kimball.watermark_window import (
|
|
22
|
+
render_pre_hook,
|
|
23
|
+
render_window_predicate,
|
|
24
|
+
)
|
|
25
|
+
from kiri_dbt_bridge.silver_kimball.zone import resolve_silver_schema
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from kiri_core import CanonicalContract, Project
|
|
29
|
+
from kiri_core.lifecycle import WatermarkWindow
|
|
30
|
+
|
|
31
|
+
from kiri_dbt_bridge.gold.dialects import GoldDialect
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _check_all_columns_have_lineage(contract: CanonicalContract) -> None:
|
|
35
|
+
"""Same fail-closed check as fact_generator: every column must
|
|
36
|
+
declare a non-empty lineage."""
|
|
37
|
+
schema = contract.primary_schema
|
|
38
|
+
if schema is None:
|
|
39
|
+
raise ValueError(f"Silver-Kimball flat {contract.contract.name!r} has no schema.")
|
|
40
|
+
missing: list[str] = []
|
|
41
|
+
empty: list[str] = []
|
|
42
|
+
for prop in schema.properties:
|
|
43
|
+
key = f"{schema.name}.{prop.name}"
|
|
44
|
+
col_meta = contract.kiri.properties.get(key)
|
|
45
|
+
if col_meta is None or col_meta.lineage is None:
|
|
46
|
+
missing.append(prop.name)
|
|
47
|
+
continue
|
|
48
|
+
if not col_meta.lineage.from_columns:
|
|
49
|
+
empty.append(prop.name)
|
|
50
|
+
if missing:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Silver-Kimball flat {contract.contract.name!r}: column(s) "
|
|
53
|
+
f"{missing} have no kiri.lineage.from_columns declared."
|
|
54
|
+
)
|
|
55
|
+
if empty:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
f"Silver-Kimball flat {contract.contract.name!r}: column(s) "
|
|
58
|
+
f"{empty} have empty kiri.lineage.from_columns — flat tables "
|
|
59
|
+
"have no 'originates here' columns."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def generate_flat_model(
|
|
64
|
+
contract: CanonicalContract,
|
|
65
|
+
project: Project,
|
|
66
|
+
dialect: GoldDialect,
|
|
67
|
+
window: WatermarkWindow | None = None,
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Render the flat silver dbt model SQL.
|
|
70
|
+
|
|
71
|
+
Backwards compatible (ADR 0080 "backwards compat"): ``window=None`` — or a
|
|
72
|
+
contract that does not declare ``kiri.watermark`` — yields the byte-identical
|
|
73
|
+
``materialized='table'`` full-build SQL this generator always produced. When a
|
|
74
|
+
``window`` is supplied AND the contract declares ``kiri.watermark``, the model
|
|
75
|
+
becomes ``materialized='incremental'`` with the store-derived window injected
|
|
76
|
+
into a ``{% if is_incremental() %}`` WHERE predicate (ADR 0080 §3/§4, Slice B).
|
|
77
|
+
|
|
78
|
+
Incremental strategy is ``append`` paired with a ``pre_hook`` window-delete
|
|
79
|
+
(NOT a keyless ``delete+insert``). Flat silver carries no business/unique key
|
|
80
|
+
(the validator forbids ``business_keys`` on ``kind=flat``), and in the
|
|
81
|
+
installed dbt-duckdb/dbt-databricks macros ``delete+insert``/``merge`` only
|
|
82
|
+
delete when a ``unique_key`` is set — a keyless ``delete+insert`` silently
|
|
83
|
+
degrades to a plain append → duplicates on a held-on-failure re-run or a
|
|
84
|
+
``--reset`` backfill, violating ADR 0017's safe-re-run guarantee. Instead the
|
|
85
|
+
model appends and a ``pre_hook`` unconditionally deletes exactly the
|
|
86
|
+
``(lower, upper]`` window from ``{{ this }}`` (guarded by
|
|
87
|
+
``{% if is_incremental() %}``) before the append, so a re-run is a true
|
|
88
|
+
delete-then-reinsert (no duplicates). ``pre_hook`` + ``append`` behave
|
|
89
|
+
identically on dbt-databricks and dbt-duckdb (ADR 0130); see
|
|
90
|
+
:func:`kiri_dbt_bridge.silver_kimball.watermark_window.render_pre_hook`.
|
|
91
|
+
"""
|
|
92
|
+
silver = contract.kiri.silver
|
|
93
|
+
if silver.kind is not GoldKind.FLAT:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
f"generate_flat_model called with kind={silver.kind!r}; "
|
|
96
|
+
"this generator only handles kiri.silver.kind=flat."
|
|
97
|
+
)
|
|
98
|
+
_check_all_columns_have_lineage(contract)
|
|
99
|
+
plan = build_silver_lineage_plan(contract, project)
|
|
100
|
+
schema = resolve_silver_schema(contract, project)
|
|
101
|
+
|
|
102
|
+
projection_lines = [
|
|
103
|
+
render_property_projection(contract, silver_col, ref, plan)
|
|
104
|
+
for silver_col, ref in plan.column_sources
|
|
105
|
+
]
|
|
106
|
+
if not projection_lines:
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"Silver-Kimball flat {contract.contract.name!r} produced no projection lines."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Incremental ONLY when a window is supplied AND the contract declares a
|
|
112
|
+
# watermark. Otherwise the byte-identical full-table build (backwards compat).
|
|
113
|
+
incremental = window is not None and contract.kiri.watermark is not None
|
|
114
|
+
|
|
115
|
+
if incremental:
|
|
116
|
+
assert window is not None # narrowed by `incremental`
|
|
117
|
+
pre_hook = render_pre_hook(contract, window, dialect)
|
|
118
|
+
config = (
|
|
119
|
+
f"{{{{ config(materialized='incremental', incremental_strategy='append'"
|
|
120
|
+
f"{pre_hook}, "
|
|
121
|
+
f"schema='{schema}') }}}}\n"
|
|
122
|
+
f"-- Generated from silver-Kimball contract {contract.contract.name!r} "
|
|
123
|
+
f"(flat — Kimball opt-out, incremental over a store-derived watermark "
|
|
124
|
+
f"window, ADR 0080 §3/§4). append + pre_hook window-delete (idempotent "
|
|
125
|
+
f"re-run: the pre_hook deletes exactly the (lower, upper] window from "
|
|
126
|
+
f"this before the append — no duplicate append; a keyless delete+insert "
|
|
127
|
+
f"would NOT delete). Target-native SQL via the "
|
|
128
|
+
f"{dialect.name!r} dialect helper (ADR 0099).\n"
|
|
129
|
+
)
|
|
130
|
+
predicate = render_window_predicate(contract, window, dialect, plan, shape="flat")
|
|
131
|
+
else:
|
|
132
|
+
config = (
|
|
133
|
+
f"{{{{ config(materialized='table', schema='{schema}') }}}}\n"
|
|
134
|
+
f"-- Generated from silver-Kimball contract {contract.contract.name!r} "
|
|
135
|
+
f"(flat — Kimball opt-out). Target-native SQL via the "
|
|
136
|
+
f"{dialect.name!r} dialect helper (ADR 0099).\n"
|
|
137
|
+
)
|
|
138
|
+
predicate = ""
|
|
139
|
+
|
|
140
|
+
body = "select\n " + ",\n ".join(projection_lines) + "\n" + from_clause(plan) + "\n"
|
|
141
|
+
if predicate:
|
|
142
|
+
body += predicate + "\n"
|
|
143
|
+
return config + "\n" + body
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def write_flat_model(
|
|
147
|
+
contract: CanonicalContract,
|
|
148
|
+
project: Project,
|
|
149
|
+
dialect: GoldDialect,
|
|
150
|
+
output_dir: Path,
|
|
151
|
+
window: WatermarkWindow | None = None,
|
|
152
|
+
) -> Path:
|
|
153
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
path = output_dir / f"{contract.contract.name}.sql"
|
|
155
|
+
path.write_text(generate_flat_model(contract, project, dialect, window), encoding="utf-8")
|
|
156
|
+
return path
|