sql-code-graph 1.4.0__tar.gz → 1.4.1__tar.gz
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.
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/PKG-INFO +1 -1
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/pyproject.toml +1 -1
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/__init__.py +1 -1
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/analyze.py +8 -1
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/base.py +82 -0
- sql_code_graph-1.4.1/tests/integration/test_analyze_case_fold.py +234 -0
- sql_code_graph-1.4.1/tests/integration/test_bare_column_cte_lineage.py +332 -0
- sql_code_graph-1.4.1/tests/integration/test_v141_surface_guards.py +490 -0
- sql_code_graph-1.4.1/tests/unit/test_analyze_case_fold.py +229 -0
- sql_code_graph-1.4.1/tests/unit/test_graph_completeness_invariant.py +188 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/uv.lock +1 -1
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/api-documenter.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/architect-planner.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/architect-reviewer.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/code-reviewer.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/developer.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/plan-reviewer.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.claude/agents/sprint-planner.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/workflows/benchmark.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/workflows/e2e-tests.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/workflows/release.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.github/workflows/test.yml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.gitignore +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.pre-commit-config.yaml +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/.sqlcgignore +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/ARCHITECTURE_REVIEW.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/CHANGELOG.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/CLAUDE.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/README.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/docs/AIRBNB_PARSE_REPORT.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/docs/cli.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/e2e_firstuser_report.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/e2e_run_20260528_101413.output +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/main.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/WORKFLOW.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/blueprint.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/bundle_claude_skill.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/feature_34_unused_presentation_segregation.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/feature_35_external_downstream_injection.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/feature_F2_bundle_claude_skill.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/feature_kuzu_to_duckdb_migration.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_downstream_sink_location.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_dynamic_table_parsing.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_expand_qualify_perf.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_firstuser_findings.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_issue29_live_test_followups.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/fix_schema_case_mismatch.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/hygiene_config_path_and_survivors.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/investigation_e5_e4.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/living_codebase_resync.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/measurements/schema_comparison_with_schema.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/measurements/schema_comparison_without_schema.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/measurements/sprint_pool_300s_plan.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/parse_diagnostics.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/parsing_errors_experiment.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/snowflake_en_test_suite.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_01.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_01_deployment_pypi.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_02.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_02_v0.3.0_core.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_03.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_03_v0.3.1_postmortem.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_04_column_lineage.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_04_column_lineage_fix.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_05_star_resolution.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_06_lineage_coverage.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_07_open_ecodes.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_07_perf_and_live_test.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_08_perf_upsert.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_09_lineage_coverage.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_10_anchor_tools.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_11_v1.0.2_bugfix.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_12_v1.1.0.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_13_v1.1.0_cluster_b.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sprint_3.1_postmortem.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/sqlcg.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/trust_layer.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1.1.0_cluster_b_provenance_trust.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1.1.0_live_graph_freshness.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1.1.1_batch_upsert_perf.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1_1_2_bugfix.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1_1_3_union_cte_star.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1_2_0_read_proxy.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/plan/v1_2_1_bugfix.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/profile.html +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/pyrightconfig.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/scripts/collect_parse_errors.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/scripts/generate_cli_docs.sh +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/__main__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/db.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/find.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/gain.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/git.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/index.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/install.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/mcp.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/reindex.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/report.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/uninstall.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/commands/watch.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/cli/main.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/config.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/duckdb_backend.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/freshness.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/graph_db.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/jobs.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/queries.cypher +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/queries.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/queries.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/schema.cypher +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/core/schema.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/dbt_adapter.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/error_classify.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/git_delta.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/indexer.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/pool.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/walker.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/indexer/watcher.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/lineage/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/lineage/aggregator.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/lineage/schema_resolver.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/metrics/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/metrics/store.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/ansi_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/bigquery_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/postgres_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/registry.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/snowflake_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/parsers/tsql_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/control.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/exceptions.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/models.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/noise_filter.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/read_client.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/server.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/skill.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/tools.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/server/writer.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/utils/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/utils/hashing.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/utils/ignore.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/src/sqlcg/utils/logging.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/adversarial/200_join.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/adversarial/500_union.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/bench_indexer.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/conftest.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/tpch/q01.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/tpch/q02.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/tpch/q03.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/tpch/q04.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/benchmarks/tpch/q05.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/conftest.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_airbnb_e2e.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_cli_index.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_git_hook_install.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_golden_lineage.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_mcp_lifecycle.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_mcp_tools.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_star_resolution_e2e.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/e2e/test_watch.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/raw_listings.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/src_hosts.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/src_listings.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/airbnb/src_reviews.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/bigquery/.gitkeep +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/duckdb_parity/kuzu_reference.json +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/jaffle_shop/customers.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/jaffle_shop/orders.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/snowflake/base_tables.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/snowflake/reports.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/snowflake/views.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/star_corpus/etl_star.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/synthetic/base_tables.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/synthetic/reports.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/fixtures/synthetic/views.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/snowflake/test_insert_select.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_T34_presentation_segregation.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_T35_external_consumers.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_anchor_tools.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_bulk_upsert.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_column_lineage_e2e.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_cross_file_lineage.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_cte_recall_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_cte_schema_alias_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_cte_source_node_invariant.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_dialect_auto_resolution.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_dialect_matrix.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_duckdb_parity.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_freshness_mcp.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_indexer_batching.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_indexer_commits.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_indexer_to_graph.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_live_anchors.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_mvcc_rebuild.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_parse_diagnostics.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_pr1_confidence_reason.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_pr2_source_location.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_pr3_kind_tagging.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_read_via_server.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_readonly_under_lock.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_reindex_via_server.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_resync.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_single_writer_queue.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_star_resolution.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_temp_table_lineage.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/integration/test_user_surface_recall_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/perf/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/perf/test_perf.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E10/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E11/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E12/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E12/e12_json_path.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E12/test_e12.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E13/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E14/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E15/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E16/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E16/e16_merge.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E16/test_e16.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E17/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E18/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E18/e18_decode.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E18/e18_nvl2.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E18/test_e18.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E19/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E2/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E2/e2_function_alias.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E2/test_e2.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E20/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E21/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E21/test_e21.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E22/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E23/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E23/test_e23.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E24/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E25/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E25/e25_cross_db.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E25/e25_two_part.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E25/test_e25.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E25/test_e25_full_id.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E26/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E27/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E27/e27_udf.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E27/test_e27.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E28/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E29/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E3/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E3/e3_alter_table.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E3/test_e3.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E30/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E31/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E32/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E33/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E34/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E35/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E36/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E36/e36_temp_table.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E36/test_e36.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E36/test_e36_xfile.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E37/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E38/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/e4_unpivot.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E4/test_e4.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/test_e5.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E8/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E8/e8_uuid.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E8/test_e8.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E9/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_aggregates/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/README.md +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/fixture_etl.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/fixture_source.sql +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/conftest.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/snowflake/test_plan_review_gates.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/snowflake/test_scripting_noise.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_BugB_escalation_uses_init_path.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_BugC_hook_upgrade.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_F2_install_skill.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_F2_skill_render.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_F2_uninstall_skill.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_T09_01_qualify_once.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_T09_02_ddl_skip.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_T09_06_log_verbosity.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_T35_config_external_consumers.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_aggregator.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_aggregator_skip.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_base_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_branch_monitor.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_bulk_upsert_invariant.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_canonical_target_resolution.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_cli.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_cli_help.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_closure_depth.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_column_lineage_wiring.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_config.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_data_models.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_db_info.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_dominant_cause.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_duckdb_backend.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_duckdb_backend_shared.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_find_cmd.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_firstuser_findings.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_freshness_helper.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_gain_ratio.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_git_delta.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_git_hooks.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_git_hooks_notify.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_graph_backend.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_hard_kill_pool.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_hygiene_config_warning.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_include_working_tree.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_index_cmd.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_index_flags.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_index_progress.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_indexer_progress.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_indexer_quality.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_install.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_install_message.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_jobs.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_judgement.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_lineage_conversion.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_literal_column_skip.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_mcp_best_practices.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_mcp_control.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_mcp_stdio_smoke.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_metrics.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_noise_filter.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_parse_file_dependency_filter.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_parse_quality.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_parser.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_perf_scaling_guard.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_pr07_observability.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_queries_loader.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_read_client.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_schema_resolver.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_server.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_sprint_06_t04_t05.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_star_resolution_unit.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_star_schema_unit.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_submit_feedback.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_subprocess_isolate.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_t02_expression_name_extraction.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_t03_ddl_skip.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_timeout_cancel.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_tools_hints.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_tools_warnings.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_uninstall.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_unqualified_fallback.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_upsert_batch_invariant.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_walker.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_watcher.py +0 -0
- {sql_code_graph-1.4.0 → sql_code_graph-1.4.1}/tests/unit/test_writer_queue.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql-code-graph
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: SQL code graph analyzer and lineage tracer
|
|
5
5
|
Project-URL: Homepage, https://github.com/Warhorze/sql-code-graph
|
|
6
6
|
Project-URL: Repository, https://github.com/Warhorze/sql-code-graph
|
|
@@ -139,6 +139,7 @@ def upstream( # noqa: B008
|
|
|
139
139
|
console.print("[red]Error: --depth must be between 1 and 100[/red]")
|
|
140
140
|
raise typer.Exit(1)
|
|
141
141
|
|
|
142
|
+
ref = ref.lower() # graph keys are lowercased at index time (C2 normalization)
|
|
142
143
|
sql = _upstream_sql(depth, include_intermediate)
|
|
143
144
|
results = run_read_routed(sql, {"ref": ref})
|
|
144
145
|
if not results and len(ref.split(".")) >= 3:
|
|
@@ -175,6 +176,7 @@ def downstream( # noqa: B008
|
|
|
175
176
|
console.print("[red]Error: --depth must be between 1 and 100[/red]")
|
|
176
177
|
raise typer.Exit(1)
|
|
177
178
|
|
|
179
|
+
ref = ref.lower() # graph keys are lowercased at index time (C2 normalization)
|
|
178
180
|
sql = _downstream_sql(depth, include_intermediate)
|
|
179
181
|
results = run_read_routed(sql, {"ref": ref})
|
|
180
182
|
if not results and len(ref.split(".")) >= 3:
|
|
@@ -291,7 +293,12 @@ def unused(
|
|
|
291
293
|
|
|
292
294
|
|
|
293
295
|
def _bare_ref(ref: str) -> str:
|
|
294
|
-
"""Strip schema prefix from a ref string, keeping table.column.
|
|
296
|
+
"""Strip schema prefix from a ref string, keeping table.column.
|
|
297
|
+
|
|
298
|
+
Lowercases defensively so this is safe to call even if the caller did not
|
|
299
|
+
first fold the ref — graph keys are lowercased at index time (C2 normalization).
|
|
300
|
+
"""
|
|
301
|
+
ref = ref.lower()
|
|
295
302
|
parts = ref.split(".")
|
|
296
303
|
if len(parts) >= 3:
|
|
297
304
|
return ".".join(parts[1:])
|
|
@@ -515,6 +515,32 @@ class SqlParser(ABC):
|
|
|
515
515
|
_walk(root)
|
|
516
516
|
return edges
|
|
517
517
|
|
|
518
|
+
def _table_node_to_ref(self, table_node: Any) -> "TableRef | None":
|
|
519
|
+
"""Convert a sqlglot exp.Table AST node to a TableRef.
|
|
520
|
+
|
|
521
|
+
Used by the #49 mis-bind override to enumerate candidate source tables
|
|
522
|
+
from a CTE body's FROM/JOIN once per CTE body (before the per-projection
|
|
523
|
+
loop). Does NOT call qualify/build_scope — preserves the O(1)-per-body
|
|
524
|
+
perf invariant.
|
|
525
|
+
|
|
526
|
+
Schema aliases are applied via _apply_table_alias so the emitted edges
|
|
527
|
+
carry the canonical (post-alias) table identity.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
table_node: sqlglot.expressions.Table AST node
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
TableRef with catalog/db/name extracted and alias-resolved, or None
|
|
534
|
+
if the alias resolution returns None (treated as an unresolvable ref).
|
|
535
|
+
"""
|
|
536
|
+
return self._apply_table_alias(
|
|
537
|
+
TableRef(
|
|
538
|
+
catalog=table_node.catalog if table_node.catalog else None,
|
|
539
|
+
db=table_node.db if table_node.db else None,
|
|
540
|
+
name=table_node.name if table_node.name else str(table_node),
|
|
541
|
+
)
|
|
542
|
+
)
|
|
543
|
+
|
|
518
544
|
def _lineage_node_to_table_ref(self, node: Any) -> "TableRef | None":
|
|
519
545
|
"""Extract a TableRef from a sqlglot LineageNode's source attribute.
|
|
520
546
|
|
|
@@ -987,6 +1013,20 @@ class SqlParser(ABC):
|
|
|
987
1013
|
# string for every column rather than re-serializing O(N_cols) times.
|
|
988
1014
|
cte_body_sql = cte_body.sql(dialect=self.DIALECT)
|
|
989
1015
|
|
|
1016
|
+
# Compute the candidate source-table set ONCE per CTE body
|
|
1017
|
+
# (before the per-projection loop) — never inside it.
|
|
1018
|
+
# Uses find_all(exp.Table) on the already-built AST; does NOT
|
|
1019
|
+
# call qualify/build_scope, preserving O(1) per CTE body.
|
|
1020
|
+
# This set is reused across all projections to detect ambiguity
|
|
1021
|
+
# (#49 mis-bind override).
|
|
1022
|
+
cte_source_tables: list[TableRef] = [
|
|
1023
|
+
ref
|
|
1024
|
+
for t in cte_body.find_all(exp.Table)
|
|
1025
|
+
if t.name # skip anonymous / subquery placeholders
|
|
1026
|
+
for ref in (self._table_node_to_ref(t),)
|
|
1027
|
+
if ref is not None
|
|
1028
|
+
]
|
|
1029
|
+
|
|
990
1030
|
# For each projection in the CTE, extract lineage.
|
|
991
1031
|
# Only iterate projections from left branch for column names, but pass
|
|
992
1032
|
# entire Union body to sg_lineage so sqlglot resolves both branches.
|
|
@@ -1010,6 +1050,48 @@ class SqlParser(ABC):
|
|
|
1010
1050
|
)
|
|
1011
1051
|
if not cte_col_name or cte_col_name == "*":
|
|
1012
1052
|
continue
|
|
1053
|
+
|
|
1054
|
+
# #49 mis-bind override: detect bare (unqualified) columns
|
|
1055
|
+
# in a ≥2-table CTE body.
|
|
1056
|
+
#
|
|
1057
|
+
# sqlglot's sg_lineage (called with no schema and no scope)
|
|
1058
|
+
# re-qualifies the CTE body internally and mis-binds bare
|
|
1059
|
+
# columns to the last-joined table — emitting a confident
|
|
1060
|
+
# WRONG edge (confirmed live: bare `m` from fact_daily was
|
|
1061
|
+
# bound to dim_time). This is not a missing edge; it is an
|
|
1062
|
+
# incorrect one.
|
|
1063
|
+
#
|
|
1064
|
+
# Override: when any bare column appears inside the projection
|
|
1065
|
+
# expression AND the CTE body has ≥2 source tables, skip
|
|
1066
|
+
# sg_lineage for this projection and instead emit one
|
|
1067
|
+
# CTE_PROJECTION_AMBIGUOUS edge per candidate source table
|
|
1068
|
+
# (confidence=0.5). Over-attribution is the safe failure mode
|
|
1069
|
+
# for impact analysis; a wrong single edge is not acceptable.
|
|
1070
|
+
#
|
|
1071
|
+
# Single-table bodies: no ambiguity; existing path unchanged.
|
|
1072
|
+
# Qualified columns in any body: no bare columns; existing path.
|
|
1073
|
+
bare_cols_in_expr = [
|
|
1074
|
+
c
|
|
1075
|
+
for c in cte_col_expr.find_all(exp.Column)
|
|
1076
|
+
if not c.table # bare = no qualifier
|
|
1077
|
+
]
|
|
1078
|
+
if bare_cols_in_expr and len(cte_source_tables) >= 2:
|
|
1079
|
+
# Emit one ambiguous edge per candidate source table.
|
|
1080
|
+
# bare_col.name is the column name to attribute;
|
|
1081
|
+
# for aggregates/CASE the bare col name is the leaf.
|
|
1082
|
+
bare_col_name = bare_cols_in_expr[0].name or cte_col_name
|
|
1083
|
+
dst_col_ref = ColumnRef(cte_dst_table, cte_col_name)
|
|
1084
|
+
for src_tbl in cte_source_tables:
|
|
1085
|
+
edges.append(
|
|
1086
|
+
LineageEdge(
|
|
1087
|
+
src=ColumnRef(src_tbl, bare_col_name),
|
|
1088
|
+
dst=dst_col_ref,
|
|
1089
|
+
transform="CTE_PROJECTION_AMBIGUOUS",
|
|
1090
|
+
confidence=0.5,
|
|
1091
|
+
)
|
|
1092
|
+
)
|
|
1093
|
+
continue # skip sg_lineage for this projection
|
|
1094
|
+
|
|
1013
1095
|
try:
|
|
1014
1096
|
# No schema: resolver.as_dict() {table:[cols]} triggers
|
|
1015
1097
|
# sqlglot nesting-level errors on fresh string parses.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""#50 — analyze upstream/downstream case-fold integration tests (PR-2).
|
|
2
|
+
|
|
3
|
+
Confirmed live on the DWH (v1.4.0): ``analyze upstream "BA.WTFE_INKOOP_ORDER_IGDC.TA_HASH"``
|
|
4
|
+
returned "No results" while the lowercase form returned the full upstream table.
|
|
5
|
+
Root cause: ``analyze.py`` upstream/downstream and ``_bare_ref`` did not lowercase the
|
|
6
|
+
``ref`` argument before querying — graph keys are lowercased at index time (C2 normalization).
|
|
7
|
+
|
|
8
|
+
Fix (PR-2): ``ref = ref.lower()`` at the top of both command functions, plus a defensive
|
|
9
|
+
``ref = ref.lower()`` inside ``_bare_ref``.
|
|
10
|
+
|
|
11
|
+
These integration tests use a real DuckDB in-memory graph. They assert on the
|
|
12
|
+
**observable returned id sets** (not "no exception"), using a small fixture with a
|
|
13
|
+
real upstream chain.
|
|
14
|
+
|
|
15
|
+
Entry-point parity audit (recorded here per plan PR-2):
|
|
16
|
+
- CLI ``analyze.py`` upstream/downstream: fixed by this PR (were missing ``.lower()``).
|
|
17
|
+
- CLI ``analyze.py`` _bare_ref: defensive ``.lower()`` added by this PR.
|
|
18
|
+
- MCP ``tools.py``: already fully case-folded via ``_parse_column_ref`` (line ~323,
|
|
19
|
+
``col_ref.lower()``) and direct ``.lower()`` calls at lines ~798, ~845, ~902, ~978,
|
|
20
|
+
~1725. No MCP changes required.
|
|
21
|
+
- CLI ``find.py``: already folds at lines 19 and 41.
|
|
22
|
+
- CLI ``analyze.py`` ``impact`` (table-level): out of #50's stated scope; uses table
|
|
23
|
+
names not column refs. Flagged as a separate follow-up if needed.
|
|
24
|
+
|
|
25
|
+
After this PR, CLI analyze upstream/downstream and MCP are parity-correct on case folding.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from unittest.mock import patch
|
|
31
|
+
|
|
32
|
+
import pytest
|
|
33
|
+
from typer.testing import CliRunner
|
|
34
|
+
|
|
35
|
+
from sqlcg.cli.commands.analyze import _bare_ref
|
|
36
|
+
from sqlcg.cli.main import app
|
|
37
|
+
from sqlcg.core.duckdb_backend import DuckDBBackend
|
|
38
|
+
from sqlcg.indexer.indexer import Indexer
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Fixture SQL corpus
|
|
42
|
+
#
|
|
43
|
+
# Simple 2-hop upstream chain:
|
|
44
|
+
# mart.fact_enriched.m ← staging.src_raw.val
|
|
45
|
+
# Through a single CTE hop.
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
_DDL_SQL = """\
|
|
49
|
+
CREATE TABLE mart.fact_enriched (m NUMBER, k VARCHAR);
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
_ETL_SQL = """\
|
|
53
|
+
INSERT INTO mart.fact_enriched (m, k)
|
|
54
|
+
WITH raw AS (SELECT val AS m, key AS k FROM staging.src_raw)
|
|
55
|
+
SELECT m, k FROM raw;
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
runner = CliRunner()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture
|
|
62
|
+
def db():
|
|
63
|
+
"""Fresh in-memory DuckDB backend with schema initialised."""
|
|
64
|
+
backend = DuckDBBackend(":memory:")
|
|
65
|
+
backend.init_schema()
|
|
66
|
+
yield backend
|
|
67
|
+
backend.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.fixture
|
|
71
|
+
def indexed_db(db, tmp_path):
|
|
72
|
+
"""Index the fixture corpus; return the backend."""
|
|
73
|
+
(tmp_path / "ddl.sql").write_text(_DDL_SQL)
|
|
74
|
+
(tmp_path / "etl.sql").write_text(_ETL_SQL)
|
|
75
|
+
Indexer().index_repo(tmp_path, dialect=None, db=db, use_git=False)
|
|
76
|
+
return db
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# Helpers
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _run_upstream(db: DuckDBBackend, ref: str) -> list[dict]:
|
|
85
|
+
"""Invoke analyze upstream via Typer, routing run_read_routed to the in-memory db.
|
|
86
|
+
|
|
87
|
+
Patches run_read_routed to call db.run_read directly, so we exercise the
|
|
88
|
+
real SQL against the indexed in-memory graph — while the command function's
|
|
89
|
+
``ref = ref.lower()`` case-fold is in effect.
|
|
90
|
+
"""
|
|
91
|
+
from sqlcg.cli.commands.analyze import _upstream_sql
|
|
92
|
+
|
|
93
|
+
def _route(sql: str, params: dict, db_path=None) -> list[dict]:
|
|
94
|
+
return db.run_read(sql, params)
|
|
95
|
+
|
|
96
|
+
with patch("sqlcg.cli.commands.analyze.run_read_routed", side_effect=_route):
|
|
97
|
+
with patch("sqlcg.server.noise_filter.NoiseFilter.from_config") as mock_nf:
|
|
98
|
+
nf = mock_nf.return_value
|
|
99
|
+
nf.is_noise.return_value = False
|
|
100
|
+
runner.invoke(app, ["analyze", "upstream", ref])
|
|
101
|
+
|
|
102
|
+
# Return raw rows from the DB using the lowercased ref (the command path result).
|
|
103
|
+
query = _upstream_sql(5, include_intermediate=False)
|
|
104
|
+
return db.run_read(query, {"ref": ref.lower()})
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _run_downstream(db: DuckDBBackend, ref: str) -> list[dict]:
|
|
108
|
+
"""Invoke analyze downstream via Typer, routing run_read_routed to the in-memory db."""
|
|
109
|
+
from sqlcg.cli.commands.analyze import _downstream_sql
|
|
110
|
+
|
|
111
|
+
def _route(sql: str, params: dict, db_path=None) -> list[dict]:
|
|
112
|
+
return db.run_read(sql, params)
|
|
113
|
+
|
|
114
|
+
with patch("sqlcg.cli.commands.analyze.run_read_routed", side_effect=_route):
|
|
115
|
+
with patch("sqlcg.server.noise_filter.NoiseFilter.from_config") as mock_nf:
|
|
116
|
+
nf = mock_nf.return_value
|
|
117
|
+
nf.is_noise.return_value = False
|
|
118
|
+
runner.invoke(app, ["analyze", "downstream", ref])
|
|
119
|
+
|
|
120
|
+
query = _downstream_sql(5, include_intermediate=False)
|
|
121
|
+
return db.run_read(query, {"ref": ref.lower()})
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# Tests — upstream case-fold (real graph, observable ids)
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_uppercase_upstream_anchor_returns_same_ids_as_lowercase(indexed_db):
|
|
130
|
+
"""UPPERCASE anchor returns the identical non-empty upstream id set as lowercase.
|
|
131
|
+
|
|
132
|
+
This is the confirmed DWH regression: uppercase ref passed to run_read_routed
|
|
133
|
+
found nothing because graph keys are stored lowercase. After the fix
|
|
134
|
+
(ref = ref.lower() at top of upstream()), the fold happens before the query.
|
|
135
|
+
|
|
136
|
+
We test observably: both forms produce the same non-empty id set.
|
|
137
|
+
"""
|
|
138
|
+
lowercase_rows = _run_upstream(indexed_db, "mart.fact_enriched.m")
|
|
139
|
+
uppercase_rows = _run_upstream(indexed_db, "MART.FACT_ENRICHED.M")
|
|
140
|
+
|
|
141
|
+
lowercase_ids = {r["id"] for r in lowercase_rows}
|
|
142
|
+
uppercase_ids = {r["id"] for r in uppercase_rows}
|
|
143
|
+
|
|
144
|
+
assert lowercase_ids, (
|
|
145
|
+
"Baseline lowercase upstream returned no results — fixture or indexer issue."
|
|
146
|
+
)
|
|
147
|
+
assert uppercase_ids, (
|
|
148
|
+
"UPPERCASE upstream returned no results while lowercase returned results — "
|
|
149
|
+
"the case-fold fix (ref = ref.lower()) is missing from analyze.upstream()."
|
|
150
|
+
)
|
|
151
|
+
assert uppercase_ids == lowercase_ids, (
|
|
152
|
+
f"UPPERCASE and lowercase upstream returned different id sets.\n"
|
|
153
|
+
f" lowercase: {sorted(lowercase_ids)}\n"
|
|
154
|
+
f" uppercase: {sorted(uppercase_ids)}"
|
|
155
|
+
)
|
|
156
|
+
# Both must contain the physical source column from the fixture.
|
|
157
|
+
assert "staging.src_raw.val" in lowercase_ids, (
|
|
158
|
+
f"Expected staging.src_raw.val in upstream ids.\nGot: {sorted(lowercase_ids)}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_mixedcase_upstream_anchor_returns_same_ids_as_lowercase(indexed_db):
|
|
163
|
+
"""Mixed-case anchor (e.g. 'Mart.Fact_Enriched.M') returns the same upstream ids."""
|
|
164
|
+
lowercase_rows = _run_upstream(indexed_db, "mart.fact_enriched.m")
|
|
165
|
+
mixedcase_rows = _run_upstream(indexed_db, "Mart.Fact_Enriched.M")
|
|
166
|
+
|
|
167
|
+
lowercase_ids = {r["id"] for r in lowercase_rows}
|
|
168
|
+
mixedcase_ids = {r["id"] for r in mixedcase_rows}
|
|
169
|
+
|
|
170
|
+
assert mixedcase_ids, "Mixed-case upstream returned no results — the case-fold fix is missing."
|
|
171
|
+
assert mixedcase_ids == lowercase_ids, (
|
|
172
|
+
f"Mixed-case and lowercase upstream returned different id sets.\n"
|
|
173
|
+
f" lowercase: {sorted(lowercase_ids)}\n"
|
|
174
|
+
f" mixed-case: {sorted(mixedcase_ids)}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# Tests — downstream case-fold (real graph, observable ids)
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_uppercase_downstream_anchor_returns_same_ids_as_lowercase(indexed_db):
|
|
184
|
+
"""UPPERCASE anchor returns the identical non-empty downstream id set as lowercase."""
|
|
185
|
+
lowercase_rows = _run_downstream(indexed_db, "staging.src_raw.val")
|
|
186
|
+
uppercase_rows = _run_downstream(indexed_db, "STAGING.SRC_RAW.VAL")
|
|
187
|
+
|
|
188
|
+
lowercase_ids = {r["id"] for r in lowercase_rows}
|
|
189
|
+
uppercase_ids = {r["id"] for r in uppercase_rows}
|
|
190
|
+
|
|
191
|
+
assert lowercase_ids, (
|
|
192
|
+
"Baseline lowercase downstream returned no results — fixture or indexer issue."
|
|
193
|
+
)
|
|
194
|
+
assert uppercase_ids, (
|
|
195
|
+
"UPPERCASE downstream returned no results while lowercase returned results — "
|
|
196
|
+
"the case-fold fix (ref = ref.lower()) is missing from analyze.downstream()."
|
|
197
|
+
)
|
|
198
|
+
assert uppercase_ids == lowercase_ids, (
|
|
199
|
+
f"UPPERCASE and lowercase downstream returned different id sets.\n"
|
|
200
|
+
f" lowercase: {sorted(lowercase_ids)}\n"
|
|
201
|
+
f" uppercase: {sorted(uppercase_ids)}"
|
|
202
|
+
)
|
|
203
|
+
assert "mart.fact_enriched.m" in lowercase_ids, (
|
|
204
|
+
f"Expected mart.fact_enriched.m in downstream ids.\nGot: {sorted(lowercase_ids)}"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# ---------------------------------------------------------------------------
|
|
209
|
+
# Tests — _bare_ref defensive lower
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_bare_ref_lowercases_3part_uppercase_ref():
|
|
214
|
+
"""_bare_ref applied to a 3-part UPPERCASE ref returns a lowercase bare ref.
|
|
215
|
+
|
|
216
|
+
The defensive ``ref = ref.lower()`` inside ``_bare_ref`` ensures the helper is
|
|
217
|
+
safe to call independently of the caller's folding, matching graph key casing.
|
|
218
|
+
"""
|
|
219
|
+
result = _bare_ref("BA.WTFE_INKOOP_ORDER_IGDC.TA_HASH")
|
|
220
|
+
assert result == "wtfe_inkoop_order_igdc.ta_hash", (
|
|
221
|
+
f"_bare_ref did not lowercase: got '{result}'"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_bare_ref_lowercases_mixedcase_ref():
|
|
226
|
+
"""_bare_ref on a mixed-case 3-part ref returns a lowercase bare ref."""
|
|
227
|
+
result = _bare_ref("Mart.Fact_Enriched.M")
|
|
228
|
+
assert result == "fact_enriched.m", f"_bare_ref did not lowercase: got '{result}'"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_bare_ref_already_lowercase_is_unchanged():
|
|
232
|
+
"""_bare_ref on an already-lowercase ref is idempotent."""
|
|
233
|
+
result = _bare_ref("mart.fact_enriched.m")
|
|
234
|
+
assert result == "fact_enriched.m"
|