sql-code-graph 1.36.1__tar.gz → 1.36.2__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.36.1 → sql_code_graph-1.36.2}/PKG-INFO +1 -1
- sql_code_graph-1.36.2/plan/metrics/gain_1.36.2_f6a335a.json +33 -0
- sql_code_graph-1.36.2/plan/metrics/gain_drop_alter_phantom_baseline_v1361.json +30 -0
- sql_code_graph-1.36.2/plan/sprints/fix_drop_alter_phantom_source_node.md +551 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/pyproject.toml +1 -1
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/__init__.py +1 -1
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/ansi_parser.py +16 -2
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_incremental_reindex_parity.py +102 -0
- sql_code_graph-1.36.2/tests/integration/test_phantom_drop_alter_node.py +123 -0
- sql_code_graph-1.36.2/tests/unit/test_phantom_drop_alter_source.py +166 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/uv.lock +1 -1
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/api-documenter.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/architect-planner.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/architect-reviewer.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/code-reviewer.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/developer.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/plan-reviewer.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.claude/agents/sprint-planner.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/workflows/benchmark.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/workflows/e2e-tests.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/workflows/release.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.github/workflows/test.yml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.gitignore +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.pre-commit-config.yaml +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/.sqlcgignore +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/ARCHITECTURE_REVIEW.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/ARCHITECTURE_REVIEW_ARCHIVE.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/CHANGELOG.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/CLAUDE.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/README.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/docs/AIRBNB_PARSE_REPORT.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/docs/cli.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/docs/getting-started.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/docs/releasing-pypi.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/e2e_firstuser_report.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/e2e_run_20260528_101413.output +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/main.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/WORKFLOW.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/pr2_catalog_load_eval.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/pr3_repo_native_plateau.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/pr4_transform_kinds.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/pr5_extraction_recall_taxonomy.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/createkind_dwh_after.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/createkind_dwh_before.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/gain_1.29.0_05c6943.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/gain_1.30.0_2c8ac25.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/gain_1.30.1_26271fc.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/gain_1.31.0_a40c837.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/gain_1.32.0_c1bec3c.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/identifier_live_acceptance.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/identifier_live_after_v1.36.0.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/identifier_live_before_v1.35.3.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/lineage5_dwh_after_pr5.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/lineage5_dwh_before_master.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr4_star_promote_after.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr4_star_promote_before.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr_b_phantom_after.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr_b_phantom_before.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr_c_merge_after.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/pr_c_merge_before.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/schema_comparison_with_schema.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/schema_comparison_without_schema.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/sprint_08_changelogs_fullindex.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/sprint_08_fullcorpus_index.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/metrics/sprint_pool_300s_plan.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/column_coverage_diagnostic.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/column_coverage_findings.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/dwh_graph_analysis.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/dwh_positional_insert_column_blindspot.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/feature_acceptance_dwh.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/graph_health_sprint_postmortem.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/island_lever_live_verification.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/issue38_pr2_live_acceptance.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/issue38_pr3_live_acceptance.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/lineage_identity_sprint_postmortem.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/pr_impact_followups_2026-06-14.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/sprint_03_v0.3.1_postmortem.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/sprint_3.1_postmortem.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/reports/v1_14_dialect_query_config_postmortem.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/blueprint.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/coverage_parse_failure_diagnosis.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/investigation_e5_e4.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/observed_usage_overlay_query_history_tableau.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/parse_diagnostics.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/parsing_errors_experiment.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/snowflake_en_test_suite.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/sql_only_coverage_lever_post_pr2.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/research/sqlcg.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/bundle_claude_skill.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/ci_tests_pool_flake_fix.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/column_lineage_recall_metric.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/coverage_p1_p3_p4.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/coverage_p1_p5_metric.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/coverage_parse_failures.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/diff-impact-producer-file.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/e8_dual_emission.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/feature_34_unused_presentation_segregation.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/feature_35_external_downstream_injection.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/feature_F2_bundle_claude_skill.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/feature_generic_var_name_resolution.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/feature_kuzu_to_duckdb_migration.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_backward_self_heal_index_at_sha.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_downstream_sink_location.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_dynamic_table_parsing.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_expand_qualify_perf.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_firstuser_findings.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_issue29_live_test_followups.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/fix_schema_case_mismatch.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/gain_coverage_metrics.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/graph_health_catalog_and_metrics.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/hygiene_config_path_and_survivors.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/issue-38-backfill-cte-bridge.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/issue-38-residual-source-extraction.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/issue-38-selects-from-island-lever.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/living_codebase_resync.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/mcp_server_self_healing.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/positional_insert_clone_blindspot.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_01.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_01_deployment_pypi.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_02.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_02_v0.3.0_core.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_03.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_04_column_lineage.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_04_column_lineage_fix.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_05_star_resolution.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_06_lineage_coverage.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_07_open_ecodes.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_07_perf_and_live_test.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_08_perf_upsert.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_09_lineage_coverage.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_10_anchor_tools.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_11_v1.0.2_bugfix.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_12_v1.1.0.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_13_v1.1.0_cluster_b.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_lineage_identity_and_session_context.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/sprint_postmortem_fixes.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/temp_table_namespacing.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/trust_layer.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/unfilled_table_impact.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1.1.0_cluster_b_provenance_trust.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1.1.0_live_graph_freshness.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1.1.1_batch_upsert_perf.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1.14.0_dialect_and_query_config_fixes.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1_1_2_bugfix.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1_1_3_union_cte_star.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1_2_0_read_proxy.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/v1_2_1_bugfix.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/plan/sprints/version-parity-and-restart.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/profile.html +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/pyrightconfig.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/scripts/collect_parse_errors.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/scripts/column_coverage_check.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/scripts/generate_cli_docs.sh +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/__main__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/analyze.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/catalog.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/db.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/find.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/gain.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/git.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/index.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/install.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/mcp.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/reindex.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/report.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/uninstall.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/viz.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/commands/watch.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/coverage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/cli/main.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/config.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/duckdb_backend.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/freshness.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/graph_db.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/jobs.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/noise_match.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/queries.cypher +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/queries.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/queries.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/schema.cypher +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/core/schema.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/dbt_adapter.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/error_classify.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/git_delta.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/indexer.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/pool.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/walker.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/indexer/watcher.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/lineage/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/lineage/aggregator.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/lineage/schema_resolver.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/metrics/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/metrics/store.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/base.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/bigquery_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/dynamic_name.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/postgres_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/registry.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/snowflake_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/parsers/tsql_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/control.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/exceptions.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/models.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/noise_filter.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/read_client.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/selfheal.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/server.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/skill.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/tools.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/server/writer.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/utils/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/utils/hashing.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/utils/ignore.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/utils/logging.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/assets/force-graph.min.js +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/assets/template.html +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/data.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/render.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/src/sqlcg/viz/tags.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/adversarial/200_join.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/adversarial/500_union.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/bench_indexer.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/conftest.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/tpch/q01.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/tpch/q02.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/tpch/q03.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/tpch/q04.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/benchmarks/tpch/q05.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/conftest.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/conftest.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_airbnb_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_cli_index.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_empty_impact_cli_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_git_hook_install.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_golden_lineage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_mcp_lifecycle.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_mcp_tools.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_pr_impact_cli_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_selfheal_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_star_resolution_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_viz_cli_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/e2e/test_watch.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/raw_listings.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/src_hosts.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/src_listings.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/airbnb/src_reviews.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/bigquery/.gitkeep +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/duckdb_parity/kuzu_reference.json +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/jaffle_shop/customers.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/jaffle_shop/orders.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/snowflake/base_tables.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/snowflake/reports.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/snowflake/views.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/star_corpus/etl_star.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/synthetic/base_tables.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/synthetic/reports.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/fixtures/synthetic/views.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/snowflake/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/snowflake/test_identifier_var_sink_resolution_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/snowflake/test_insert_select.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/snowflake/test_qualify_bare_tables_command_guard_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_T34_presentation_segregation.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_T35_external_consumers.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_analyze_case_fold.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_anchor_tools.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_backfill_impact_consistency.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_bare_column_cte_lineage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_bulk_upsert.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_case_split_seed_regression.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_catalog_kind_upgrade.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_catalog_load_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cli_index_clear_before_rebuild.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_column_coverage_patterns.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_column_lineage_e2e.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_coverage_metrics_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cross_file_lineage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cte_key_namespacing_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cte_recall_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cte_schema_alias_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_cte_source_node_invariant.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_dialect_auto_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_dialect_matrix.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_duckdb_parity.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_empty_impact_blast_radius.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_empty_index_rollback_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_freshness_mcp.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_gating_join_retrofit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_identity_counters_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_impact_consumer_parity.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_index_degraded_files_metric.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_index_filter_node_exclusion.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_indexer_batching.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_indexer_commits.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_indexer_to_graph.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_issue38_cte_insert_regression.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_join_col_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_key_normalization_chokepoint.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_live_anchors.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_mvcc_rebuild.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_non_table_create_coverage_no_regression.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_non_table_create_kind_in_graph.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_parse_diagnostics.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_phantom_non_table_create_node.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_pr1_confidence_reason.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_pr2_source_location.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_pr3_kind_tagging.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_pr_impact_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_qualify_failed_persist.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_read_via_server.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_readonly_under_lock.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_reindex_via_server.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_repo_relative_cte_namespaces.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_resolvable_write_col_edges_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_resync.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_selects_from_completeness.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_selects_from_cte_body_source.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_selects_from_subscope_source.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_single_writer_queue.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_skip_counts_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_star_promote_temp_columns.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_star_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_temp_table_lineage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_temp_table_namespacing.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_tool_version_stamp_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_usage_derived_catalog.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_use_schema_session_context_integration.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_user_surface_recall_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_v141_surface_guards.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_v1_14_0_dialect_and_query_config_fixes.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_view_kind_in_graph.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_viz_data_build.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/integration/test_write_memory_ceiling.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/perf/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/perf/test_perf.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E10/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E11/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E12/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E12/e12_json_path.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E12/test_e12.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E13/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E14/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E15/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E16/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E16/e16_merge.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E16/test_e16.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E17/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E18/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E18/e18_decode.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E18/e18_nvl2.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E18/test_e18.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E19/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E2/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E2/e2_function_alias.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E2/test_e2.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E20/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E21/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E21/test_e21.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E22/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E23/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E23/test_e23.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E24/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E25/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E25/e25_cross_db.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E25/e25_two_part.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E25/test_e25.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E25/test_e25_full_id.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E26/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E27/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E27/e27_udf.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E27/test_e27.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E28/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E29/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E3/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E3/e3_alter_table.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E3/test_e3.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E30/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E31/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E32/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E33/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E34/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E35/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E36/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E36/e36_temp_table.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E36/test_e36.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E36/test_e36_xfile.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E37/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E38/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/e4_unpivot.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E4/test_e4.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/test_e5.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E8/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E8/e8_uuid.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E8/test_e8.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E9/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_aggregates/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/README.md +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/fixture_etl.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/fixture_source.sql +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/conftest.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/snowflake/test_plan_review_gates.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/snowflake/__init__.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/snowflake/test_qualify_bare_tables_command_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/snowflake/test_scripting_noise.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_BugB_escalation_uses_init_path.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_BugC_hook_upgrade.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_F2_install_skill.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_F2_skill_render.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_F2_uninstall_skill.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_T09_01_qualify_once.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_T09_02_ddl_skip.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_T09_06_log_verbosity.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_T35_config_external_consumers.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_aggregator.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_aggregator_skip.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_analyze_case_fold.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_base_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_branch_monitor.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_bulk_upsert_invariant.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_canonical_target_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_catalog_csv_parsing.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_catalog_missing_warning.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_classify_non_table_create_kind.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_cli.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_cli_doc_flag_staleness.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_cli_help.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_clone_positional_insert_blindspot.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_closure_depth.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_column_lineage_wiring.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_config.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_coverage_metrics.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_cte_key_namespacing.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_cte_source_gap_metric.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_data_models.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_db_info.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_db_info_coverage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_db_path_isolation_fixture.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_doc_links.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_doc_markdown_link_existence.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_dominant_cause.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_duckdb_backend.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_duckdb_backend_shared.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_dynamic_name_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_e5_view_alias_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_e8_dual_emission.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_e8_temp_chain_key_mismatch.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_empty_propagation_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_extract_select_output_columns.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_file_ignore_defaults.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_find_cmd.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_firstuser_findings.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_freshness_helper.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_gain_coverage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_gain_ratio.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_gating_join_field_docstrings.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_git_delta.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_git_hooks.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_git_hooks_notify.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_graph_backend.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_graph_completeness_invariant.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_hard_kill_pool.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_has_column_precedence_upsert.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_hook_reindex_detach.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_hygiene_config_warning.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_identifier_var_sink_resolution.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_identity_counters.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_include_working_tree.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_index_cmd.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_index_filter_config.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_index_flags.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_index_progress.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_index_summary_degraded_metric.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_indexer_progress.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_indexer_quality.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_install.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_install_message.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_issue_63_readonly_lock.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_jobs.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_join_col_resolve_marker.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_judgement.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_lineage_conversion.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_literal_column_skip.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_mcp_best_practices.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_mcp_control.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_mcp_stdio_smoke.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_merge_column_lineage.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_metrics.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_noise_filter.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_normalize_keys.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_parse_failed_classification.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_parse_file_dependency_filter.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_parse_quality.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_parser.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_perf_scaling_guard.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_phantom_non_table_create_source.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_pr07_observability.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_pr6_execute_immediate_unwrap.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_pr_impact_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_qualify_failed_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_queries_loader.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_read_client.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_repo_relative_cte_namespaces.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_resolvable_write_col_edges_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_resolve_join_columns_sql.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_schema_resolver.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_selects_from_completeness_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_selfheal_detector.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_selfheal_pr1_messages.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_selfheal_reexec.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_selfheal_watcher.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_server.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_skip_counts_persistence.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_sprint_06_t04_t05.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_star_resolution_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_star_schema_unit.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_submit_feedback.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_subprocess_isolate.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_t02_expression_name_extraction.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_t03_ddl_skip.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_temp_table_namespacing.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_timeout_cancel.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_tool_version_stamp.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_tools_hints.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_tools_warnings.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_transform_kind_classification.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_uninstall.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_unknown_sentinel_skip.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_unqualified_fallback.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_upsert_batch_invariant.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_use_schema_session_context.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_version_parity.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_view_classification.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_viz_config_schemas.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_viz_facets.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_viz_render_self_contained.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_walker.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_watcher.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/tests/unit/test_worker_error_classification.py +0 -0
- {sql_code_graph-1.36.1 → sql_code_graph-1.36.2}/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.36.
|
|
3
|
+
Version: 1.36.2
|
|
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
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{"total_calls": 1791, "last_7d_calls": 1791, "index_runs": 5, "feedback_tp": 0,
|
|
2
|
+
"feedback_total": 0, "top_tools": [{"name": "execute_sql", "count": 604},
|
|
3
|
+
{"name": "trace_column_lineage", "count": 470}, {"name":
|
|
4
|
+
"list_dialects_and_repos", "count": 154}], "execute_sql_ratio": 0.34,
|
|
5
|
+
"parse_quality": {"sqlglot": 5162, "scripting": 20, "dynamic_sql": 3},
|
|
6
|
+
"coverage": {"catalogued_tables": 5892, "total_tables": 6553, "good_edges":
|
|
7
|
+
54998, "total_edges": 63088, "phantom_edges": 12346, "blindspot_tables": 633,
|
|
8
|
+
"good_edges_strict": 49453, "edge_health_strict_pct": 78.39,
|
|
9
|
+
"good_edges_scoped": 37305, "total_edges_scoped": 37628,
|
|
10
|
+
"edge_health_scoped_pct": 99.14, "phantom_confirmed": 11686,
|
|
11
|
+
"phantom_contradicted": 239, "phantom_unverified": 421, "top_blindspot_tables":
|
|
12
|
+
[{"table": "ba.wtfa_kpi_datum_klant", "bad_edges": 70}, {"table":
|
|
13
|
+
"ba.wtfs_voorraad_dagstand", "bad_edges": 55}, {"table":
|
|
14
|
+
"ba.wtfs_voorraad_zondagstand", "bad_edges": 55}, {"table":
|
|
15
|
+
"wtfe_kpi_voorraad_artikel_voorraadlocatie", "bad_edges": 32}, {"table":
|
|
16
|
+
"ba.wtda_toeleverancier", "bad_edges": 13}, {"table":
|
|
17
|
+
"ba.wtfs_voorraad_dagstand_webshop_periode", "bad_edges": 13}, {"table":
|
|
18
|
+
"da.etl_rtwbs_cadeaukaarten", "bad_edges": 12}, {"table":
|
|
19
|
+
"wtfe_kpi_schapbeschikbaarheid_voorraadlocatie", "bad_edges": 12}, {"table":
|
|
20
|
+
"da.ttint_inventdim_formule", "bad_edges": 10}, {"table":
|
|
21
|
+
"ba.wtdm_klant_herkomst", "bad_edges": 8}], "blindspot_tables_for_80pct": 11,
|
|
22
|
+
"files_indexed": 1335, "indexed_sha":
|
|
23
|
+
"fdf1b551a34601a6cf3ce1c8b9f76e27ce2753e6", "db_path":
|
|
24
|
+
"/tmp/sqlcg_dropfix_f6a335a.db", "index_timestamp": 1781505432.175605,
|
|
25
|
+
"degraded_parse_total": 5185, "degraded_parse_queries": 27,
|
|
26
|
+
"degraded_parse_by_dir": {"etl": {"degraded": 8, "total": 2772}, "ddl":
|
|
27
|
+
{"degraded": 19, "total": 2413}}, "zero_edge_write_queries": 956,
|
|
28
|
+
"total_write_queries": 2322, "cte_key_collisions": 71,
|
|
29
|
+
"rescuable_unqualified_edges": 125, "info_schema_has_column_rows": 100193,
|
|
30
|
+
"cte_source_gap_writes": 0, "resolvable_write_col_edges": 38068,
|
|
31
|
+
"transitive_col_edges": 5312, "qualify_failed_statements": 9,
|
|
32
|
+
"tool_version_stale": false, "indexed_tool_version": "1.36.2",
|
|
33
|
+
"running_tool_version": "1.36.2"}}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{"total_calls": 1721, "last_7d_calls": 1721, "index_runs": 5, "feedback_tp": 0,
|
|
2
|
+
"feedback_total": 0, "top_tools": [{"name": "execute_sql", "count": 580},
|
|
3
|
+
{"name": "trace_column_lineage", "count": 452}, {"name":
|
|
4
|
+
"list_dialects_and_repos", "count": 148}], "execute_sql_ratio": 0.34,
|
|
5
|
+
"parse_quality": {"sqlglot": 5166, "scripting": 20, "dynamic_sql": 3},
|
|
6
|
+
"coverage": {"catalogued_tables": 5864, "total_tables": 6673, "good_edges":
|
|
7
|
+
53811, "total_edges": 61983, "phantom_edges": 12241, "blindspot_tables": 631,
|
|
8
|
+
"good_edges_strict": 48907, "edge_health_strict_pct": 78.9, "good_edges_scoped":
|
|
9
|
+
37502, "total_edges_scoped": 38063, "edge_health_scoped_pct": 98.53,
|
|
10
|
+
"phantom_confirmed": 11701, "phantom_contradicted": 352, "phantom_unverified":
|
|
11
|
+
188, "top_blindspot_tables": [{"table": "ba.wtfa_kpi_datum_klant", "bad_edges":
|
|
12
|
+
70}, {"table": "ba.wtfs_voorraad_dagstand", "bad_edges": 55}, {"table":
|
|
13
|
+
"ba.wtfs_voorraad_zondagstand", "bad_edges": 55}, {"table":
|
|
14
|
+
"da.rtgmd_postnl_customer_usage", "bad_edges": 43}, {"table":
|
|
15
|
+
"da.rtgmd_postnl_athome_3s", "bad_edges": 33}, {"table":
|
|
16
|
+
"wtfe_kpi_voorraad_artikel_voorraadlocatie", "bad_edges": 32}, {"table":
|
|
17
|
+
"da.rtgmd_postnl_facturen", "bad_edges": 30}, {"table": "da.hthyb_pages",
|
|
18
|
+
"bad_edges": 23}, {"table": "da.rtgmd_dhb_extra_kosten", "bad_edges": 19},
|
|
19
|
+
{"table": "da.hthyb_page_brandplacement", "bad_edges": 17}],
|
|
20
|
+
"blindspot_tables_for_80pct": 18, "files_indexed": 1335, "indexed_sha":
|
|
21
|
+
"fdf1b551a34601a6cf3ce1c8b9f76e27ce2753e6", "db_path": "/tmp/ab_v1361.db",
|
|
22
|
+
"index_timestamp": 1781502931.191137, "degraded_parse_total": 5189,
|
|
23
|
+
"degraded_parse_queries": 27, "degraded_parse_by_dir": {"etl": {"degraded": 8,
|
|
24
|
+
"total": 2776}, "ddl": {"degraded": 19, "total": 2413}},
|
|
25
|
+
"zero_edge_write_queries": 969, "total_write_queries": 2323,
|
|
26
|
+
"cte_key_collisions": 0, "rescuable_unqualified_edges": 125,
|
|
27
|
+
"info_schema_has_column_rows": 100193, "cte_source_gap_writes": 0,
|
|
28
|
+
"resolvable_write_col_edges": 37713, "transitive_col_edges": 4558,
|
|
29
|
+
"qualify_failed_statements": 9, "tool_version_stale": false,
|
|
30
|
+
"indexed_tool_version": "1.36.1", "running_tool_version": "1.36.1"}}
|
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
# Feature Plan: DROP/ALTER phantom source-node + edge suppression (#161 part 2)
|
|
2
|
+
|
|
3
|
+
**Status:** REVIEWED
|
|
4
|
+
**Owner:** architect-planner
|
|
5
|
+
**Version target:** PATCH. Master is currently `v1.36.1`; this is a graph-correctness
|
|
6
|
+
fix that only *removes* phantom nodes/edges and adds no public surface (no new CLI
|
|
7
|
+
flag, MCP tool, node/edge type, or config key), so per [CLAUDE.md](../../CLAUDE.md)
|
|
8
|
+
SemVer it is a **patch** bump. Reconcile the exact number against `master` at the
|
|
9
|
+
moment the PR is cut: if master is still `v1.36.1` → ship `v1.36.2`; if a later patch
|
|
10
|
+
has landed, take the next patch above whatever master then carries. Do not assume
|
|
11
|
+
`1.36.2` blindly.
|
|
12
|
+
**Cross-refs:** issue #161 (phantom source node for non-table DDL), #159 (create-kind
|
|
13
|
+
catch-all → OTHER), #166 / PR-B (the non-table-CREATE source-gate that this extends).
|
|
14
|
+
[`sprint_snowflake_lineage_patterns.md`](sprint_snowflake_lineage_patterns.md) PR-B.
|
|
15
|
+
|
|
16
|
+
## Summary
|
|
17
|
+
|
|
18
|
+
`DROP TABLE/VIEW ... <name>` and `ALTER TABLE <name> ...` statements mint a phantom
|
|
19
|
+
`SqlTable` source node (+ a phantom `SELECTS_FROM` edge) for their *target* object,
|
|
20
|
+
even though DROP/ALTER carry no data flow. This inflates the graph with degree-0
|
|
21
|
+
island nodes (the viz finding) and ~1,308 phantom `SELECTS_FROM` edges on the live
|
|
22
|
+
DWH corpus. The fix extends the existing #161 single source-gate in
|
|
23
|
+
`AnsiParser._parse_statement` to also return `sources = []` for `exp.Drop` /
|
|
24
|
+
`exp.Alter` / `exp.TruncateTable`.
|
|
25
|
+
|
|
26
|
+
## Live-verification verdict (DONE before planning — evidence below)
|
|
27
|
+
|
|
28
|
+
**VERDICT: hypothesis CONFIRMED at the mechanism level; hypothesis #2 (the #159
|
|
29
|
+
attribution) PARTIALLY DISPROVEN — #159 is NOT the cause. Plan proceeds.**
|
|
30
|
+
|
|
31
|
+
### Mechanism confirmed (file:line)
|
|
32
|
+
|
|
33
|
+
For `DROP VIEW IF EXISTS IA_ANALYTICS.BA_WTDA_CAMPAIGN`:
|
|
34
|
+
- `sqlglot` parses it to `exp.Drop`, `kind="VIEW"`, and `AnsiParser._classify` →
|
|
35
|
+
`"OTHER"` (verified by probe).
|
|
36
|
+
- `build_scope(stmt)` returns `None` for `exp.Drop`/`exp.Alter` → control falls into
|
|
37
|
+
the `else` fallback branch in
|
|
38
|
+
[`ansi_parser.py:580-595`](../../src/sqlcg/parsers/ansi_parser.py) which calls
|
|
39
|
+
`_fallback_table_scan(stmt)`.
|
|
40
|
+
- [`_fallback_table_scan`](../../src/sqlcg/parsers/ansi_parser.py) (`ansi_parser.py:792`)
|
|
41
|
+
does `find_all(exp.Table)`, which matches the DROP/ALTER *target* table and returns
|
|
42
|
+
it as a "source".
|
|
43
|
+
- The #161 gate `is_non_table_create = isinstance(stmt, exp.Create) and stmt.kind not
|
|
44
|
+
in ("TABLE","VIEW")` (ansi_parser.py:566) is **False** for `exp.Drop`/`exp.Alter`
|
|
45
|
+
(they are not `exp.Create`), so the gate does NOT cover them.
|
|
46
|
+
- The "source" flows into `out.referenced_tables` via the unconditional
|
|
47
|
+
`out.referenced_tables.extend(query_node.sources)` at
|
|
48
|
+
[`ansi_parser.py:246`](../../src/sqlcg/parsers/ansi_parser.py) /
|
|
49
|
+
[`snowflake_parser.py:894`](../../src/sqlcg/parsers/snowflake_parser.py) /
|
|
50
|
+
[`:1054`](../../src/sqlcg/parsers/snowflake_parser.py), minting a `SqlTable` node +
|
|
51
|
+
`SELECTS_FROM` edge.
|
|
52
|
+
|
|
53
|
+
### Decisive corpus case
|
|
54
|
+
|
|
55
|
+
[`ddl/changelogs/BA-TABLES/WTDA_MARKETING_AUTOMATION_CAMPAIGN.sql`] line 9 is the ONLY
|
|
56
|
+
appearance of `IA_ANALYTICS.BA_WTDA_CAMPAIGN` in the file — a `DROP VIEW IF EXISTS`.
|
|
57
|
+
The CREATEs in that file target `BA.WTDA_MARKETING_AUTOMATION_CAMPAIGN`. Live
|
|
58
|
+
`parse_file` output:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
stmt: OTHER target=None sources=['ia_analytics.ba_wtda_campaign'] <- DROP VIEW
|
|
62
|
+
stmt: CREATE_VIEW target=ia_analytics.ba_wtda_marketing_automation_campaign ...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
So `ia_analytics.ba_wtda_campaign` can ONLY originate from the DROP. Confirmed present
|
|
66
|
+
as a degree-0 island in the live graph.
|
|
67
|
+
|
|
68
|
+
### Why `_exclude_synthetic_tables` does NOT catch them
|
|
69
|
+
|
|
70
|
+
[`_exclude_synthetic_tables`](../../src/sqlcg/server/tools.py) (tools.py:388) keys on
|
|
71
|
+
`SqlTable.kind in {"cte","derived","temp"}` (`_SYNTHETIC_TABLE_KINDS`, tools.py:383). A
|
|
72
|
+
DROP-minted source reference is a plain `kind="table"` node (the default source-ref
|
|
73
|
+
kind in [`indexer.py` `_flush_row_batch`](../../src/sqlcg/indexer/indexer.py)), so it
|
|
74
|
+
is never in the synthetic set. **Two reasons the `_bck*` filter misses these:**
|
|
75
|
+
1. The node's `kind` is `"table"`, not a synthetic kind → the filter passes it through.
|
|
76
|
+
2. `_exclude_synthetic_tables` is a **query-time** filter inside MCP tools
|
|
77
|
+
(get_change_scope / diff_impact / …). The viz reads `SqlTable` rows directly via
|
|
78
|
+
[`viz/data.py` `_Q_NODES`](../../src/sqlcg/viz/data.py) and emits a node for EVERY
|
|
79
|
+
`SqlTable` regardless of kind — the filter never runs for the viz at all.
|
|
80
|
+
|
|
81
|
+
The corpus `.sqlcg.toml` has `noise_filter.ignore_table_regexes = ["_bck"]`, which
|
|
82
|
+
strips some `_bck` nodes at index time, which is why the viz showed ~136 and not the
|
|
83
|
+
full count — but the mechanism still leaks every non-`_bck` DROP/ALTER target.
|
|
84
|
+
|
|
85
|
+
### Quantification (live DWH, 1,451 files, current worktree v1.36.1)
|
|
86
|
+
|
|
87
|
+
| Metric | Value | Source |
|
|
88
|
+
|--------|-------|--------|
|
|
89
|
+
| Total `SqlTable` nodes | 6,673 | `_Q_NODES` |
|
|
90
|
+
| Degree-0 island nodes (viz definition, all schemas) | 3,747 | viz logic replica |
|
|
91
|
+
| `SqlTable` nodes whose **sole** origin is the DROP/ALTER source loop | **169** | corpus re-parse |
|
|
92
|
+
| — of which `_bck*` backup | 7 | (most already stripped by noise_filter) |
|
|
93
|
+
| — of which `da.rtgmd_*` | 32 | all sole-DROP-origin (see below) |
|
|
94
|
+
| — `_obsolete` | 2 | |
|
|
95
|
+
| — other | 128 | |
|
|
96
|
+
| Phantom `SELECTS_FROM` edges from OTHER-kind no-target queries | **1,308** | (21% of all 6,239 SELECTS_FROM) |
|
|
97
|
+
|
|
98
|
+
The "169 sole-DROP-origin nodes" closely matches the finding's "136 island tables"
|
|
99
|
+
(difference = noise_filter `_bck` stripping + viz schema scoping).
|
|
100
|
+
|
|
101
|
+
### A/B vs v1.34.1 (pre-#159) — code-level, definitive
|
|
102
|
+
|
|
103
|
+
A full reindex at v1.34.1 was **not** run (RAM at 145 MB free at the time; the
|
|
104
|
+
code-level A/B is conclusive and a reindex would add no signal for the DROP/ALTER
|
|
105
|
+
target). Evidence:
|
|
106
|
+
|
|
107
|
+
- Pre-#159 commit `2047bc0`: `_classify`'s `exp.Drop`/`exp.Alter` both hit the same
|
|
108
|
+
`case _: return QueryKind.OTHER` arm as today — **unchanged by #159**.
|
|
109
|
+
- Pre-#159 `ansi_parser.py` had the SAME `_fallback_table_scan` fallthrough for
|
|
110
|
+
Drop/Alter and NO `is_non_table_create` gate (the gate is #161, post-#159).
|
|
111
|
+
- #159 (`4a55306`) flipped ONLY the **non-table `exp.Create`** catch-all
|
|
112
|
+
`CREATE_TABLE`→`OTHER`. It did not touch `exp.Drop`/`exp.Alter` classification or
|
|
113
|
+
source extraction.
|
|
114
|
+
|
|
115
|
+
**Conclusion:** the DROP/ALTER phantom-node/edge bug is **pre-existing and independent
|
|
116
|
+
of #159**. #159 changed the *label* of non-table CREATEs (whose source node #166
|
|
117
|
+
already gated); it neither introduced nor increased the DROP/ALTER island nodes. The
|
|
118
|
+
plan corrects the finding's hypothesis #2 accordingly. The fix is still warranted on
|
|
119
|
+
the confirmed mechanism + measured 169 nodes / 1,308 edges.
|
|
120
|
+
|
|
121
|
+
### rtgmd note (finding said these should REMAIN)
|
|
122
|
+
|
|
123
|
+
The finding noted raw `da.rtgmd_*` ingest tables are genuinely lineage-free and should
|
|
124
|
+
stay. Verified: the 32 `da.rtgmd_*` nodes flagged here appear in the corpus **only** as
|
|
125
|
+
`drop table` targets (e.g. `da.rtgmd_csp_coulances` — sole appearance is
|
|
126
|
+
`ddl/changelogs/DA-TABLES/RTGMD.sql:7 drop table DA.RTGMD_CSP_COULANCES;`, no CREATE,
|
|
127
|
+
no SELECT/INSERT). They are themselves DROP-origin phantoms and SHOULD be removed.
|
|
128
|
+
rtgmd tables that have a **real** CREATE/INSERT definition elsewhere will still appear
|
|
129
|
+
(they have real edges/origins) and are unaffected by this fix — those are the genuine
|
|
130
|
+
lineage-free islands that REMAIN. The fix only removes nodes whose *sole* origin is the
|
|
131
|
+
DROP/ALTER loop.
|
|
132
|
+
|
|
133
|
+
### gain baseline (acceptance-gate framing)
|
|
134
|
+
|
|
135
|
+
Saved: [`plan/metrics/gain_drop_alter_phantom_baseline_v1361.json`](../metrics/gain_drop_alter_phantom_baseline_v1361.json).
|
|
136
|
+
|
|
137
|
+
| coverage key | baseline (v1.36.1) |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `total_tables` | 6,673 |
|
|
140
|
+
| `total_edges` | 61,983 |
|
|
141
|
+
| `good_edges` | 53,811 |
|
|
142
|
+
| `good_edges_strict` | 48,907 |
|
|
143
|
+
| `good_edges_scoped` | 37,502 |
|
|
144
|
+
| `edge_health_strict_pct` | 78.9 |
|
|
145
|
+
| `edge_health_scoped_pct` | 98.53 |
|
|
146
|
+
| `phantom_edges` | 12,241 |
|
|
147
|
+
| `phantom_confirmed` | 11,701 |
|
|
148
|
+
| `blindspot_tables` | 631 |
|
|
149
|
+
|
|
150
|
+
## Scope
|
|
151
|
+
|
|
152
|
+
### In Scope
|
|
153
|
+
- Suppress `sources` for `exp.Drop`, `exp.Alter`, and `exp.TruncateTable` in the single
|
|
154
|
+
source-gate in `AnsiParser._parse_statement` (the same chokepoint as #161). This
|
|
155
|
+
removes both the phantom `SqlTable` node AND the phantom `SELECTS_FROM` edge for these
|
|
156
|
+
statement kinds.
|
|
157
|
+
- Unit test pinning the new behaviour (DROP/ALTER/TRUNCATE emit no source / no
|
|
158
|
+
referenced_table), mirroring [`test_phantom_non_table_create_source.py`](../../tests/unit/test_phantom_non_table_create_source.py).
|
|
159
|
+
- Integration test (real DuckDB) pinning that a DROP-only target does NOT become a
|
|
160
|
+
`SqlTable` node and produces no `SELECTS_FROM` edge, mirroring
|
|
161
|
+
[`test_phantom_non_table_create_node.py`](../../tests/integration/test_phantom_non_table_create_node.py).
|
|
162
|
+
- Control tests proving real DML/CTAS lineage is untouched (UPDATE/DELETE/MERGE/SELECT
|
|
163
|
+
reads and CTAS sources survive), including a same-name DROP+CREATE no-collateral-loss
|
|
164
|
+
case.
|
|
165
|
+
- Incremental-parity test (extend
|
|
166
|
+
[`test_incremental_reindex_parity.py`](../../tests/integration/test_incremental_reindex_parity.py))
|
|
167
|
+
proving the gate holds on `resync_changed` / `reindex_file`, not just `index_repo`.
|
|
168
|
+
- Version bump (patch) + gain re-measure to populate the acceptance gate.
|
|
169
|
+
|
|
170
|
+
### Non-Goals
|
|
171
|
+
- **Schema qualification** — `ia_analytics.ba_wtda_*` / `ia_tableau.ba_wtda_*` are REAL
|
|
172
|
+
distinct corpus tables (publish layers prefix the source business-area into the
|
|
173
|
+
name). The qualification is CORRECT. Do not "fix" it.
|
|
174
|
+
- **Quoted-identifier islands** (`ia_tableau.prijsadvies aan huis`) — that is #153,
|
|
175
|
+
out of scope here.
|
|
176
|
+
- **rtgmd tables with real definitions** — left as genuine lineage-free islands.
|
|
177
|
+
- Any change to `_exclude_synthetic_tables` or the viz (the fix is upstream, at parse
|
|
178
|
+
time — the right place; no downstream filter is added).
|
|
179
|
+
- Re-classifying DROP/ALTER away from `OTHER` (the kind label is fine; only the
|
|
180
|
+
source-extraction is wrong).
|
|
181
|
+
|
|
182
|
+
## Design
|
|
183
|
+
|
|
184
|
+
### Root cause (one chokepoint)
|
|
185
|
+
[`AnsiParser._parse_statement`](../../src/sqlcg/parsers/ansi_parser.py) (ansi_parser.py:566)
|
|
186
|
+
currently gates only non-table CREATEs:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
is_non_table_create = isinstance(stmt, exp.Create) and stmt.kind not in ("TABLE", "VIEW")
|
|
190
|
+
...
|
|
191
|
+
if is_non_table_create:
|
|
192
|
+
sources = []
|
|
193
|
+
else:
|
|
194
|
+
# build_scope / _fallback_table_scan <- DROP/ALTER target leaks in here
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`SnowflakeParser` inherits `_parse_statement` and `_classify` (it subclasses
|
|
198
|
+
`AnsiParser`, no override — verified). So this single gate feeds all three
|
|
199
|
+
`referenced_tables.extend(query_node.sources)` sites in both parsers.
|
|
200
|
+
|
|
201
|
+
### Proposed change
|
|
202
|
+
Add a sibling predicate and fold it into the same gate. DROP/ALTER/TRUNCATE never carry
|
|
203
|
+
a data-flow source (no SELECT body is structurally possible), so suppressing their
|
|
204
|
+
sources is edge-neutral for real lineage:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
# #161 part 2: DROP / ALTER / TRUNCATE reference their TARGET object, not a
|
|
208
|
+
# data-flow source. build_scope() returns None for these, so _fallback_table_scan
|
|
209
|
+
# would otherwise scoop the target name into `sources`, minting a phantom island
|
|
210
|
+
# SqlTable node + SELECTS_FROM edge (measured: 169 sole-DROP-origin nodes,
|
|
211
|
+
# 1,308 phantom SELECTS_FROM edges on the DWH corpus). Keyed off the AST type,
|
|
212
|
+
# independent of _classify.
|
|
213
|
+
is_non_flow_ddl = isinstance(stmt, (exp.Drop, exp.Alter, exp.TruncateTable))
|
|
214
|
+
...
|
|
215
|
+
if is_non_table_create or is_non_flow_ddl:
|
|
216
|
+
sources = []
|
|
217
|
+
else:
|
|
218
|
+
...
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
(Final naming/structure at developer discretion as long as it is the SAME single gate
|
|
222
|
+
and keyed off the AST type, not `_classify`.)
|
|
223
|
+
|
|
224
|
+
### CRITICAL INVARIANT — the gate is PER-STATEMENT, no collateral node loss
|
|
225
|
+
`sources = []` is set **for the single DROP/ALTER/TRUNCATE statement currently being
|
|
226
|
+
parsed in `_parse_statement`, and for nothing else.** `_parse_statement` is invoked
|
|
227
|
+
once per top-level statement (see the loop at
|
|
228
|
+
[`ansi_parser.py:212`](../../src/sqlcg/parsers/ansi_parser.py) and the Snowflake
|
|
229
|
+
override sites [`snowflake_parser.py:870`](../../src/sqlcg/parsers/snowflake_parser.py)
|
|
230
|
+
/ [`:1024`](../../src/sqlcg/parsers/snowflake_parser.py)); the `isinstance(stmt, ...)`
|
|
231
|
+
predicate is evaluated against *that one statement's* AST node. It cannot reach across
|
|
232
|
+
to another statement in the same file, nor to another file.
|
|
233
|
+
|
|
234
|
+
Consequence — the precise blast radius:
|
|
235
|
+
|
|
236
|
+
- A table whose **sole** appearance in the entire corpus is as a DROP/ALTER/TRUNCATE
|
|
237
|
+
target loses its phantom `SqlTable` node and its phantom `SELECTS_FROM` edge. This is
|
|
238
|
+
the intended removal.
|
|
239
|
+
- A table that is ALSO legitimately the target of a `CREATE TABLE/VIEW` (its CREATE
|
|
240
|
+
statement is a *different* `_parse_statement` call that still emits its real target +
|
|
241
|
+
sources), or that is read by a real `SELECT`/`INSERT…SELECT`/CTAS anywhere (in this
|
|
242
|
+
file or any other), **keeps its `SqlTable` node and all of its real edges.** The graph
|
|
243
|
+
node identity is keyed on `(schema, name)` and is created/kept by any of those real
|
|
244
|
+
origins; suppressing the DROP statement's source list removes only the edge the DROP
|
|
245
|
+
would have contributed, never the node a real statement created.
|
|
246
|
+
|
|
247
|
+
The acceptance metric is therefore **"count of sole-DROP/ALTER/TRUNCATE-origin island
|
|
248
|
+
nodes → ~0"**, NOT "every table named in a DROP is removed". A table named in both a
|
|
249
|
+
DROP and a CREATE must survive with its real edges intact — this is pinned by a
|
|
250
|
+
dedicated integration test (see Step 2.2, the mixed-origin case) and by the live
|
|
251
|
+
acceptance probe (the `da.rtgmd_*` analysis already distinguishes sole-DROP-origin
|
|
252
|
+
phantoms from rtgmd tables with a real CREATE/INSERT that REMAIN).
|
|
253
|
+
|
|
254
|
+
### All reindex paths inherit the gate (incremental == from-scratch)
|
|
255
|
+
The fix lives in `AnsiParser._parse_statement`
|
|
256
|
+
([`ansi_parser.py:423`](../../src/sqlcg/parsers/ansi_parser.py), gate block at
|
|
257
|
+
`:568`), which is the SINGLE parse chokepoint shared by every path that turns SQL into
|
|
258
|
+
`ParsedFile.referenced_tables`. Verified call routes (file:line):
|
|
259
|
+
|
|
260
|
+
- **Full index** — `index_repo` → `_index_single_file`
|
|
261
|
+
([`indexer.py:410`](../../src/sqlcg/indexer/indexer.py)) → `parser.parse_file` →
|
|
262
|
+
`_parse_statement` (loop at [`ansi_parser.py:212`](../../src/sqlcg/parsers/ansi_parser.py)).
|
|
263
|
+
- **Branch-delta incremental** — `resync_changed`
|
|
264
|
+
([`indexer.py:1010`](../../src/sqlcg/indexer/indexer.py)) re-parses its `reparse_set`
|
|
265
|
+
via the SAME `_index_single_file` → `parse_file` route (Step 4, `indexer.py:122-123`).
|
|
266
|
+
- **Single-file watch** — `reindex_file`
|
|
267
|
+
([`indexer.py:1420`](../../src/sqlcg/indexer/indexer.py)) calls `parser.parse_file`
|
|
268
|
+
directly ([`indexer.py:1458`](../../src/sqlcg/indexer/indexer.py), the
|
|
269
|
+
`parsed = parser.parse_file(...)` line).
|
|
270
|
+
- **Snowflake** — `SnowflakeParser` does not override `_parse_statement`; both its
|
|
271
|
+
override entry points call `AnsiParser._parse_statement`
|
|
272
|
+
([`snowflake_parser.py:870`](../../src/sqlcg/parsers/snowflake_parser.py),
|
|
273
|
+
[`:1024`](../../src/sqlcg/parsers/snowflake_parser.py)).
|
|
274
|
+
|
|
275
|
+
Because all four routes funnel through the one gated method, the fix holds identically
|
|
276
|
+
on the incremental path — there is no full-path-only step to drift from. This is the
|
|
277
|
+
**exact bug class of the recent #170/#171 incremental-parity regression** (a step
|
|
278
|
+
present in `index_repo` but absent in `resync_changed`). To make that guarantee
|
|
279
|
+
*falsifiable* rather than asserted, the plan extends the existing parity harness
|
|
280
|
+
[`test_incremental_reindex_parity.py`](../../tests/integration/test_incremental_reindex_parity.py)
|
|
281
|
+
with a DROP-bearing file (Step 2.3): incremental and from-scratch graphs must contain
|
|
282
|
+
the SAME `SqlTable` keys and SELECTS_FROM edges (i.e. neither path mints the phantom).
|
|
283
|
+
|
|
284
|
+
### Why `exp.TruncateTable` is included — GROUNDED, not class-analogy
|
|
285
|
+
TRUNCATE is NOT a speculative add. **Measured on the live source (file:line / probe):**
|
|
286
|
+
|
|
287
|
+
- The DWH corpus contains **514 files** with a `TRUNCATE TABLE` statement
|
|
288
|
+
(`grep -ilE 'truncate +table'` over `/home/ignwrad/Projects/dwh`), e.g.
|
|
289
|
+
`etl/pdi/template/htwfm_contract_us28254.sql` → `TRUNCATE TABLE da_tmp.htwfm_contract;`.
|
|
290
|
+
- `sqlglot.parse_one("TRUNCATE TABLE s.t", dialect="snowflake")` is `exp.TruncateTable`
|
|
291
|
+
(verified by probe; DROP→`exp.Drop`, ALTER→`exp.Alter` confirmed in the same probe).
|
|
292
|
+
- Pre-fix, the target leaks identically to DROP/ALTER — a live `SnowflakeParser.parse_file`
|
|
293
|
+
probe returned:
|
|
294
|
+
`'TRUNCATE TABLE s.truncated;' -> referenced_tables=['s.truncated']`
|
|
295
|
+
(alongside `DROP VIEW IF EXISTS s.dropped;` → `['s.dropped']` and
|
|
296
|
+
`ALTER TABLE s.altered ADD COLUMN c INT;` → `['s.altered']`).
|
|
297
|
+
|
|
298
|
+
So TRUNCATE is the same confirmed leak mechanism with a large live population, not a
|
|
299
|
+
zero-instance guard. (The base.py `_can_have_column_lineage` docstring already lists
|
|
300
|
+
TruncateTable/Drop/Alter together as structurally lineage-free — this gate is the
|
|
301
|
+
source-side analogue.) NOTE: the original "169 nodes / 1,308 edges" quantification in
|
|
302
|
+
§Quantification was the DROP/ALTER measurement; the live re-measure (Step 3.2) will
|
|
303
|
+
report the TRUNCATE contribution to the removed-node/edge count separately so the
|
|
304
|
+
TRUNCATE share is grounded post-fix, not just pre-fix.
|
|
305
|
+
|
|
306
|
+
### Stale inline comment to correct (housekeeping, do not skip)
|
|
307
|
+
The existing #161 gate comment at
|
|
308
|
+
[`ansi_parser.py:567`](../../src/sqlcg/parsers/ansi_parser.py) cites the extend sites as
|
|
309
|
+
`snowflake_parser.py:784 / :944`; the live sites are
|
|
310
|
+
[`snowflake_parser.py:894`](../../src/sqlcg/parsers/snowflake_parser.py) and
|
|
311
|
+
[`:1054`](../../src/sqlcg/parsers/snowflake_parser.py) (ansi `:246` is correct). The
|
|
312
|
+
developer updates the comment to the correct lines while editing this block.
|
|
313
|
+
|
|
314
|
+
### Performance
|
|
315
|
+
Zero hot-path impact: the predicate is one `isinstance` check per statement, evaluated
|
|
316
|
+
BEFORE the (already-skipped) `build_scope`/`_fallback_table_scan` for these kinds — it
|
|
317
|
+
actually *removes* a `_fallback_table_scan` call for every DROP/ALTER/TRUNCATE. No
|
|
318
|
+
change to `_extract_column_lineage`, `body_scope`, qualify-once, or any
|
|
319
|
+
indexer bulk/batch upsert path. All CLAUDE.md performance invariants are untouched.
|
|
320
|
+
|
|
321
|
+
The following invariant suites must pass **UNMODIFIED** (the developer must not edit,
|
|
322
|
+
skip, or relax any of them; a red here means the fix touched a hot path it must not):
|
|
323
|
+
- [`test_perf_scaling_guard.py`](../../tests/unit/test_perf_scaling_guard.py) — the
|
|
324
|
+
general O(N)→O(N²) guard.
|
|
325
|
+
- [`test_bulk_upsert_invariant.py`](../../tests/unit/test_bulk_upsert_invariant.py) —
|
|
326
|
+
`_upsert_parsed_file` uses bulk upserts.
|
|
327
|
+
- [`test_upsert_batch_invariant.py`](../../tests/unit/test_upsert_batch_invariant.py) —
|
|
328
|
+
`_flush_row_batch` flushes once per batch.
|
|
329
|
+
- [`test_T09_01_qualify_once.py`](../../tests/unit/test_T09_01_qualify_once.py) —
|
|
330
|
+
qualify/build_scope once per statement.
|
|
331
|
+
|
|
332
|
+
These are listed not because the fix is expected to touch them — it is a single
|
|
333
|
+
`isinstance` predicate added *above* the `build_scope`/`_fallback_table_scan` branch,
|
|
334
|
+
which it does not modify — but to make the no-regression contract explicit and
|
|
335
|
+
gateable.
|
|
336
|
+
|
|
337
|
+
### Data model / API
|
|
338
|
+
No schema change, no new method, no new node/edge type. No backward-compat concern
|
|
339
|
+
(re-index is the migration path). Existing phantom nodes/edges disappear on the next
|
|
340
|
+
`sqlcg index`.
|
|
341
|
+
|
|
342
|
+
### Dependencies
|
|
343
|
+
None new.
|
|
344
|
+
|
|
345
|
+
## Implementation Steps
|
|
346
|
+
|
|
347
|
+
### Phase 1: Parser source-gate
|
|
348
|
+
|
|
349
|
+
**Step 1.1** — Extend the source-gate in `_parse_statement`.
|
|
350
|
+
- Files affected: [`src/sqlcg/parsers/ansi_parser.py`](../../src/sqlcg/parsers/ansi_parser.py)
|
|
351
|
+
(the `is_non_table_create` block, ~line 566-595).
|
|
352
|
+
- Add `is_non_flow_ddl = isinstance(stmt, (exp.Drop, exp.Alter, exp.TruncateTable))`
|
|
353
|
+
and OR it into the gate so `sources = []` for those kinds.
|
|
354
|
+
- Update the existing #161 gate comment to document the DROP/ALTER/TRUNCATE extension
|
|
355
|
+
and cross-ref this plan + #161.
|
|
356
|
+
- Acceptance: a `DROP VIEW IF EXISTS s.t` QueryNode has `sources == []` and
|
|
357
|
+
`parsed.referenced_tables == []`; same for `ALTER TABLE s.t ADD COLUMN …` and
|
|
358
|
+
`TRUNCATE TABLE s.t`.
|
|
359
|
+
|
|
360
|
+
### Phase 2: Tests
|
|
361
|
+
|
|
362
|
+
**Step 2.1** — Unit test (no backend).
|
|
363
|
+
- File: new `tests/unit/test_phantom_drop_alter_source.py` (mirror the #161 unit test
|
|
364
|
+
structure; do NOT seed opaque case codes into test names).
|
|
365
|
+
- Behaviours to pin:
|
|
366
|
+
- DROP TABLE / DROP VIEW (with and without `IF EXISTS`) emit no source, no
|
|
367
|
+
referenced_table.
|
|
368
|
+
- ALTER TABLE emits no source, no referenced_table.
|
|
369
|
+
- TRUNCATE TABLE emits no source, no referenced_table.
|
|
370
|
+
- Control: `UPDATE s.t SET c = (SELECT x FROM s.src)` STILL records `s.src` as a
|
|
371
|
+
referenced source (real read preserved).
|
|
372
|
+
- Control: `DELETE FROM s.t WHERE id IN (SELECT id FROM s.src)` STILL records
|
|
373
|
+
`s.src`.
|
|
374
|
+
- Control: CTAS `CREATE TABLE s.t AS SELECT a FROM s.src` STILL records `s.src`
|
|
375
|
+
(re-assert #161's TABLE/VIEW arm is intact).
|
|
376
|
+
- Gate-keyed-on-AST documentation guard: assert `sqlglot.parse_one("DROP VIEW s.t",
|
|
377
|
+
dialect="snowflake")` is `exp.Drop`, `ALTER …` is `exp.Alter`, `TRUNCATE TABLE …`
|
|
378
|
+
is `exp.TruncateTable`.
|
|
379
|
+
|
|
380
|
+
**Step 2.2** — Integration test (real DuckDB in-memory).
|
|
381
|
+
- File: new `tests/integration/test_phantom_drop_alter_node.py` (mirror
|
|
382
|
+
[`test_phantom_non_table_create_node.py`](../../tests/integration/test_phantom_non_table_create_node.py)).
|
|
383
|
+
- The fixture MUST match the LIVE corpus shape — schema-qualified, snowflake dialect,
|
|
384
|
+
with all three statement forms exactly as they appear in the DWH:
|
|
385
|
+
`DROP VIEW IF EXISTS ia_analytics.ba_wtda_campaign;`, an `ALTER TABLE … ADD COLUMN …`,
|
|
386
|
+
and `TRUNCATE TABLE da_tmp.htwfm_contract;` (the real sampled form). Do NOT use a
|
|
387
|
+
minimal unqualified `DROP TABLE t;` that cannot exhibit the schema-qualified phantom.
|
|
388
|
+
- **Negative control (house rule — tests assert observable output AND must fail on
|
|
389
|
+
pre-fix code):** the dropped/altered/truncated-only target test must be written so it
|
|
390
|
+
FAILS against current (pre-fix) `_parse_statement` — i.e. on pre-fix code the
|
|
391
|
+
`SqlTable` node and `SELECTS_FROM` edge for the target ARE present, so the assertion
|
|
392
|
+
`node absent / edge absent` goes red. The developer must run the new test once against
|
|
393
|
+
the unpatched parser and record (in the PR/handoff) that it failed there, then green
|
|
394
|
+
after the gate. A test that is green both before and after is not a regression test.
|
|
395
|
+
- Behaviours to pin (index a tiny fixture, then query the graph):
|
|
396
|
+
- A file containing ONLY `DROP VIEW IF EXISTS ia_analytics.ba_wtda_campaign;` produces
|
|
397
|
+
NO `SqlTable` row for `ia_analytics.ba_wtda_campaign` and NO `SELECTS_FROM` edge with
|
|
398
|
+
that dst key. Same assertion for an ALTER-only and a TRUNCATE-only target.
|
|
399
|
+
- **Mixed-origin survival (the no-collateral-loss case):** a file with
|
|
400
|
+
`DROP VIEW IF EXISTS s.x;` followed by
|
|
401
|
+
`CREATE OR REPLACE VIEW s.y AS SELECT a FROM s.real;` produces a `SqlTable` row for
|
|
402
|
+
`s.y` and `s.real` (and the real `s.real → s.y` lineage), but NO node for `s.x`.
|
|
403
|
+
- **Same-name DROP + CREATE survives (per-statement invariant, explicit):** a file
|
|
404
|
+
where the SAME object is both DROP'd and re-created —
|
|
405
|
+
`DROP TABLE s.t;` then `CREATE TABLE s.t AS SELECT c FROM s.src;` — MUST keep the
|
|
406
|
+
`SqlTable` node for `s.t` AND the real `s.src → s.t` edge. Only the phantom edge the
|
|
407
|
+
DROP would have contributed is gone; the CREATE's node + edge survive. This pins
|
|
408
|
+
that the gate is per-statement and cannot delete a node a sibling statement
|
|
409
|
+
legitimately created.
|
|
410
|
+
|
|
411
|
+
**Step 2.3** — Incremental-parity test (extend the existing harness).
|
|
412
|
+
- File: extend [`test_incremental_reindex_parity.py`](../../tests/integration/test_incremental_reindex_parity.py)
|
|
413
|
+
(reuse its `_sql_table_keys` / `_selects_from_edges` helpers, `indexer.index_repo`
|
|
414
|
+
vs `indexer.resync_changed` / `indexer.reindex_file` structure at lines 156 / 235).
|
|
415
|
+
- Add a DROP-bearing file to the tree (e.g. a file with
|
|
416
|
+
`DROP VIEW IF EXISTS s.dropped_only;` and a real `CREATE … AS SELECT … FROM s.real;`)
|
|
417
|
+
and assert the incremental graph (`resync_changed` after a branch delta, and
|
|
418
|
+
separately `reindex_file`) has the SAME `SqlTable` keys and the SAME SELECTS_FROM
|
|
419
|
+
edges as a from-scratch `index_repo` of the final tree — i.e. NEITHER path mints the
|
|
420
|
+
phantom `s.dropped_only` node/edge. Guards against the #170/#171-class drift where the
|
|
421
|
+
gate would be honoured on the full path but missed on the incremental path.
|
|
422
|
+
|
|
423
|
+
### Phase 3: Version + measure
|
|
424
|
+
|
|
425
|
+
**Step 3.1** — Patch version bump.
|
|
426
|
+
- Files: [`pyproject.toml`](../../pyproject.toml), [`src/sqlcg/__init__.py`](../../src/sqlcg/__init__.py),
|
|
427
|
+
`uv lock`. Bug fix only, no added surface → **patch**. Reconcile against `master` at
|
|
428
|
+
PR-cut: master is `v1.36.1` today → ship `v1.36.2`; if a later patch has merged first,
|
|
429
|
+
take the next patch above whatever master then carries (do not hardcode `1.36.2`).
|
|
430
|
+
- Acceptance: `uv run sqlcg --version` reports the new version;
|
|
431
|
+
[`test_version_parity.py`](../../tests/unit/test_version_parity.py) stays green
|
|
432
|
+
(`pyproject.toml` == `__init__.py` == lockfile).
|
|
433
|
+
|
|
434
|
+
**Step 3.2** — Live re-measure for the acceptance gate (delegated index run).
|
|
435
|
+
- Re-index the live DWH corpus on the fix branch, capture `sqlcg gain --json` into
|
|
436
|
+
`plan/metrics/gain_drop_alter_phantom_after_<ver>.json`, and record the deltas in the
|
|
437
|
+
Acceptance Criteria table below. Index time must stay under the 5-min budget; report
|
|
438
|
+
gain table-health.
|
|
439
|
+
|
|
440
|
+
## Test Strategy
|
|
441
|
+
|
|
442
|
+
- **Unit**: assert observable `QueryNode.sources` / `ParsedFile.referenced_tables`
|
|
443
|
+
contents for DROP/ALTER/TRUNCATE (empty) and the DML/CTAS controls (real source
|
|
444
|
+
present). No "no exception raised" assertions.
|
|
445
|
+
- **Integration**: assert observable graph state — absence of the phantom `SqlTable`
|
|
446
|
+
node and absence of the phantom `SELECTS_FROM` edge for a DROP-only target, presence
|
|
447
|
+
of the real CREATE-VIEW lineage in the same fixture, and the same-name DROP+CREATE
|
|
448
|
+
survival case. Fixtures use the snowflake dialect and schema-qualified names mirroring
|
|
449
|
+
the live corpus shape (`ia_analytics.ba_wtda_*` and `da_tmp.htwfm_contract`-style),
|
|
450
|
+
per the live-config-fidelity rule. The phantom-absence assertion is a **negative
|
|
451
|
+
control**: it must be confirmed RED on pre-fix `_parse_statement` and GREEN after, so
|
|
452
|
+
it genuinely pins the removal rather than passing vacuously.
|
|
453
|
+
- **Incremental parity**: assert the incremental-path graph (`resync_changed` /
|
|
454
|
+
`reindex_file`) equals the from-scratch graph for a DROP-bearing tree (same SqlTable
|
|
455
|
+
keys, same SELECTS_FROM edges) — guards the #170/#171-class drift.
|
|
456
|
+
- **Regression guards**: existing #161 tests
|
|
457
|
+
([`test_phantom_non_table_create_source.py`](../../tests/unit/test_phantom_non_table_create_source.py),
|
|
458
|
+
[`test_phantom_non_table_create_node.py`](../../tests/integration/test_phantom_non_table_create_node.py))
|
|
459
|
+
must stay green (the TABLE/VIEW arm is unchanged). Perf scaling guard
|
|
460
|
+
([`test_perf_scaling_guard.py`](../../tests/unit/test_perf_scaling_guard.py)) must
|
|
461
|
+
stay green (no hot-path change).
|
|
462
|
+
|
|
463
|
+
## Acceptance Criteria
|
|
464
|
+
|
|
465
|
+
- [ ] A `DROP TABLE`/`DROP VIEW` statement (±`IF EXISTS`) yields `QueryNode.sources == []`
|
|
466
|
+
and contributes nothing to `ParsedFile.referenced_tables`. (unit)
|
|
467
|
+
- [ ] An `ALTER TABLE` statement yields `QueryNode.sources == []` and no
|
|
468
|
+
referenced_table. (unit)
|
|
469
|
+
- [ ] A `TRUNCATE TABLE` statement yields `QueryNode.sources == []` and no
|
|
470
|
+
referenced_table. (unit)
|
|
471
|
+
- [ ] `UPDATE … (SELECT … FROM s.src)`, `DELETE … (SELECT … FROM s.src)`, and CTAS
|
|
472
|
+
`CREATE TABLE … AS SELECT … FROM s.src` STILL record `s.src` as a source. (unit
|
|
473
|
+
controls — proves the fix is edge-neutral for real reads)
|
|
474
|
+
- [ ] Indexing a DROP-only / ALTER-only / TRUNCATE-only file produces NO `SqlTable`
|
|
475
|
+
node and NO `SELECTS_FROM` edge for the target object — AND this test was shown to
|
|
476
|
+
FAIL on pre-fix code (negative control). (integration)
|
|
477
|
+
- [ ] A file where the SAME object is both DROP'd and CREATE'd (`DROP TABLE s.t;` +
|
|
478
|
+
`CREATE TABLE s.t AS SELECT c FROM s.src;`) keeps the `s.t` node and the real
|
|
479
|
+
`s.src → s.t` edge — no collateral node loss. (integration, per-statement invariant)
|
|
480
|
+
- [ ] Incremental (`resync_changed` / `reindex_file`) and from-scratch (`index_repo`)
|
|
481
|
+
graphs of a DROP-bearing tree have identical `SqlTable` keys and SELECTS_FROM
|
|
482
|
+
edges — the gate holds on the incremental path. (integration parity)
|
|
483
|
+
- [ ] On the live DWH re-index: the `ia_analytics.ba_wtda_campaign` node is **gone**
|
|
484
|
+
(decisive case), and the count of **sole-DROP/ALTER/TRUNCATE-origin** island nodes
|
|
485
|
+
drops from 169 toward ~0 — measured as sole-origin, NOT "every DROP-named table
|
|
486
|
+
removed". Tables that also have a real CREATE/INSERT/SELECT origin MUST still be
|
|
487
|
+
present. (live; re-run the corpus re-parse probe, and report the TRUNCATE share of
|
|
488
|
+
the removed nodes/edges separately since the original 169/1,308 was DROP/ALTER)
|
|
489
|
+
- [ ] On the live DWH re-index: phantom `SELECTS_FROM` edges from OTHER-kind no-target
|
|
490
|
+
queries drops from 1,308 toward the residual (UPDATE/DELETE/MERGE/SELECT real
|
|
491
|
+
reads must remain — i.e. only the Drop/Alter/Truncate subset is removed). (live)
|
|
492
|
+
- [ ] `gain --json` after the fix: `good_edges_strict` and `good_edges_scoped` are
|
|
493
|
+
**unchanged or up** (no real lineage lost); `total_tables` and `total_edges` go
|
|
494
|
+
**down** (phantom nodes/edges removed); `edge_health_*_pct` unchanged or up.
|
|
495
|
+
Record exact before/after in
|
|
496
|
+
`plan/metrics/gain_drop_alter_phantom_after_<ver>.json`.
|
|
497
|
+
- [ ] Index time stays under the 5-minute budget.
|
|
498
|
+
- [ ] Existing #161 tests remain green. The perf/upsert invariant suites
|
|
499
|
+
(`test_perf_scaling_guard.py`, `test_bulk_upsert_invariant.py`,
|
|
500
|
+
`test_upsert_batch_invariant.py`, `test_T09_01_qualify_once.py`) pass
|
|
501
|
+
**UNMODIFIED** — not edited, skipped, or relaxed.
|
|
502
|
+
|
|
503
|
+
## Risks and Mitigations
|
|
504
|
+
|
|
505
|
+
| Risk | Mitigation |
|
|
506
|
+
|------|------------|
|
|
507
|
+
| A DROP/ALTER target that is ALSO a real table loses its node (collateral loss) | It does not: the gate is PER-STATEMENT (`_parse_statement` runs once per statement; the `isinstance` check sees only that statement). Real tables get their node from a *different* CREATE/INSERT/SELECT statement and survive with their real edges; only the phantom source the DROP statement contributed is removed. Pinned by two integration cases: mixed-origin (different objects) and same-name DROP+CREATE (same object). The live metric is "sole-DROP/ALTER/TRUNCATE-origin islands → ~0", not "all DROP-named tables removed". |
|
|
508
|
+
| Gate honoured on full index but missed on incremental reindex (#170/#171 class) | The fix is parser-level in `_parse_statement`; all of `index_repo`, `resync_changed`, and `reindex_file` route through `parse_file` → `_parse_statement` (call routes verified in Design). The extended `test_incremental_reindex_parity.py` asserts incremental == from-scratch for a DROP-bearing tree. |
|
|
509
|
+
| Suppressing TRUNCATE sources hides a real dependency | TRUNCATE has no data-flow source by definition (target-only). Edge-neutral. |
|
|
510
|
+
| Removing nodes lowers good_edges (false "regression") | good_edges count only real lineage; phantom DROP edges are not counted as good. The gain gate explicitly requires good_edges_strict/scoped unchanged-or-up. |
|
|
511
|
+
| sqlglot maps some DROP/ALTER variant to a different AST type | The unit doc-guard asserts the AST types for the dialect; if a variant differs, the gate's `isinstance` tuple is extended (cheap, local). |
|
|
512
|
+
| #153 quoted-identifier islands conflated with this fix | Explicitly out of scope; those nodes have a different origin (quoted name handling), untouched here. |
|
|
513
|
+
|
|
514
|
+
### Deviations
|
|
515
|
+
|
|
516
|
+
#### Deviation 1: UPDATE/DELETE sub-SELECT control assertions could not be used as written
|
|
517
|
+
- **Reason**: The plan (Step 2.1) specified unit controls asserting
|
|
518
|
+
`UPDATE s.t SET c = (SELECT x FROM s.src)` and
|
|
519
|
+
`DELETE FROM s.t WHERE id IN (SELECT id FROM s.src)` STILL record `s.src` as a
|
|
520
|
+
referenced source. Verified on clean (pre-fix) code: the live parser returns
|
|
521
|
+
`referenced_tables == []` for BOTH — it does not extract sub-SELECT sources from
|
|
522
|
+
UPDATE-SET / DELETE-WHERE clauses. This is pre-existing parser behaviour,
|
|
523
|
+
independent of this gate (proven by running the probe with `ansi_parser.py`
|
|
524
|
+
stashed). The asserted source was never recorded, so the control as written would
|
|
525
|
+
be red on master.
|
|
526
|
+
- **Change**: Replaced the UPDATE/DELETE sub-SELECT source controls with controls
|
|
527
|
+
the parser actually produces — `SELECT ... FROM s.src`, `INSERT INTO s.t SELECT ...
|
|
528
|
+
FROM s.src`, and the CTAS arm (`#161` intact) — and added a
|
|
529
|
+
`TestGateEdgeNeutralForNonGatedKinds` class asserting UPDATE/SELECT are outside the
|
|
530
|
+
gate's `isinstance` tuple and unperturbed. This preserves the plan's INTENT
|
|
531
|
+
(prove the gate is edge-neutral for real reads) using observable parser output.
|
|
532
|
+
- **Impact**: No scope change; the edge-neutrality guarantee is still pinned. The
|
|
533
|
+
gate's `isinstance` tuple is `(exp.Drop, exp.Alter, exp.TruncateTable)` only, so
|
|
534
|
+
UPDATE/DELETE are untouched by construction.
|
|
535
|
+
- **Date**: 2026-06-15
|
|
536
|
+
|
|
537
|
+
#### Deviation 2: Live-DWH baseline JSON is a different corpus state than the re-index
|
|
538
|
+
- **Reason**: The committed baseline `gain_drop_alter_phantom_baseline_v1361.json`
|
|
539
|
+
(total_tables=6,673) was captured against a 1,451-file DWH state. The live
|
|
540
|
+
re-index on this branch indexed the current 1,335-file checkout
|
|
541
|
+
(indexed_sha=fdf1b551). The absolute gain totals are therefore NOT a clean
|
|
542
|
+
before/after delta against that JSON (different corpus).
|
|
543
|
+
- **Change**: The acceptance evidence is reported as a controlled STRUCTURAL probe
|
|
544
|
+
on the post-fix graph (decisive node absent; sole-DROP/ALTER/TRUNCATE-origin
|
|
545
|
+
count) plus the pre-fix→post-fix red→green negative-control test, rather than a
|
|
546
|
+
raw gain-total subtraction. The after-JSON
|
|
547
|
+
(`gain_1.36.2_f6a335a.json`) is committed for the record.
|
|
548
|
+
- **Impact**: No scope change. The decisive case is verified (node count 0) and the
|
|
549
|
+
sole-origin phantom population is verified gone (all DDL targets that remain have a
|
|
550
|
+
real CREATE / CREATE-CLONE / HAS_COLUMN / lineage origin).
|
|
551
|
+
- **Date**: 2026-06-15
|