feat(sql): Phase 3 procedural SQL metrics - #257
Open
tinovyatkin wants to merge 26 commits into
Open
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Phase 3 of the SQL metrics research foundation (§6.17, §12): the
sql.procedural.*metric family for PL/SQL, T-SQL, MySQL, and BigQuery-scripting routines.New metrics
routine_count,block_count,max_block_depth,if_count,loop_count,case_statement_count,exception_handler_count,return_count,raise_throw_count,dynamic_sql_countsql.procedural.cyclomatic_complexity(Sonar's documented PL/SQL increments) andsql.procedural.cognitive_complexity(nesting-weighted, boolean-sequence rule) — both file-level and per-routine onFunctionspaces, both evidence-backed (metric == Σ contribution.amountby construction)sql.structural_complexity.max_embedded_query(§9.3) file-level; each routine's space carries its own embeddedsql.structural_complexitychange_riskdeviation:+5 × dynamic_sql_countwith asql.change_risk.dynamic_sqlreason codeMeasurement model
One dialect-agnostic token state machine over procedural regions only (routine definitions, anonymous blocks, marker-gated
Unparsableruns). Basis: empirical CST probes (recorded as parser comparison §9) showed Oracle's typed nodes, T-SQL's keyword-led statements, and unparsable MySQL/T-SQL bodies all share one classified token stream — comments/literals lex separately even insideUnparsable, so keyword scanning is trivia-safe, and one code path cannot double-count constructs that are both typed and keyword-visible. §9 also re-affirms sqruff oversqlparserv0.62 for Phases 3–4 (sqlparserhard-fails onCREATE PROCEDUREfor both MsSql and Oracle dialects).CASE-expression WHEN arms deliberately stay in the declarative
sql.case.*family (documented deviation from Sonar's single-number model).Statement classification
New
anonymous_blockkind (OracleDECLARE…BEGIN…END, T-SQLIF/WHILE/BEGINbatch statements, BigQuery scripting). Unlike routine bodies, block bodies execute when the file is applied, so their DML/TCL now feedssql.dml.*, object touches, and change risk; routine bodies stay excluded.Leave-it-better fixes
OracleUpdateStatement/OracleTableReference/… instead of the ANSI kinds; top-level Oracle DML classified asunknownand appeared in nosql.dml.*, object-touch, or change-risk metric. Dialect-foldingSyntaxSets fix every scan.sql.predicate.not_countno longer countsNOT NULLcolumn constraints orIF NOT EXISTSguards (IS NOT NULL,NOT IN,NOT EXISTSstill count).PredicateFacts.in_count(computed, never published; IN folds intocomparison_countper §6.7).dialectparameter threaded throughfacts::extract.Docs
New schoolbook page
docs/metrics/sql/procedural.mdx(worked cyclomatic example, measurement model, references); overview/roadmap updated — Phase 3 marked shipped with parser-bound limitations documented (PL/SQL cursor FOR loops and procedural CASE degrade toUnparsable, never mis-count).Testing
plsql_procedure_control_flow.sql,tsql_procedure_control_flow.sql) with hand-traced expected values asserted per family (PL/SQL: cyclomatic 12, cognitive 9, change risk 9; T-SQL through unparsable spill: cyclomatic 8, blocks 6) plus full-metric-map snapshotsnot_countexclusionscargo insta test --all-features --check --workspace --unreferenced reject --test-runner nextest→ 1685/1685 passed, no unreferenced snapshots;cargo clippy --all-targets --all-features --lockedclean; existing snapshot updates are purely additive (new zero-valued keys)