Commit Graph
25 Commits
Author SHA1 Message Date
Thom Lamb 012e693fe1 chore: refactor for sonarqube issues
SonarQube Analysis / sonarqube (pull_request) Successful in 6m3s
2026-05-22 17:13:31 -05:00
Thom Lamb 84b1f83c0f chore: working on cleaning up more dup lines
SonarQube Analysis / sonarqube (pull_request) Successful in 3m37s
2026-05-21 16:50:13 -05:00
Thom Lamb 2e483c9fa8 fix: resolving more duplicate lines issue
SonarQube Analysis / sonarqube (pull_request) Successful in 3m55s
2026-05-21 14:53:21 -05:00
Thom Lamb a43be2639e refactor(breakdowns): Extract shared collection analysis logic
SonarQube Analysis / sonarqube (pull_request) Successful in 3m12s
2026-05-21 13:45:46 -05:00
Thom Lamb 2d9148547f fix(serialization): Remove legacy BinaryFormatter support
SonarQube Analysis / sonarqube (pull_request) Successful in 4m42s
The `[Serializable]` attribute and corresponding `[OnDeserialized]` methods have been removed from various breakdown classes. This eliminates reliance on `BinaryFormatter`, which is a deprecated and insecure serialization mechanism in modern .NET.

This change also resolves SonarQube rule S5766 warnings by removing the context in which they apply, leading to cleaner and more secure code.
2026-05-20 17:44:21 -05:00
Thom Lamb e3153e58c4 fix(security): Resolve SonarQube security hotspots
SonarQube Analysis / sonarqube (pull_request) Successful in 3m9s
Introduce a default regex match timeout across the library to prevent potential ReDoS attacks (SonarQube rule S6444).
Implement `[OnDeserialized]` methods to re-establish object invariants and validate state after deserialization, addressing SonarQube rule S5766.
2026-05-20 17:19:17 -05:00
Thom Lamb 82af7b8de1 fix(query): modernize CalculationFilterGroup and adjust IsValid behavior
SonarQube Analysis / sonarqube (pull_request) Successful in 3m9s
Refactors the `CalculationFilterGroup` class to utilize C# primary constructors and property initializers, improving conciseness and readability.

The `IsValid()` method's logic is updated. Previously, it returned true if *any* filter in the group was valid. Now, it returns true only if *all* filters are valid, and an empty filter group is considered valid. This adjustment clarifies the group's validity criteria, aligning with common interpretations for `All` operations and addressing related technical debt.
2026-05-20 15:31:41 -05:00
Thom Lamb 24a9b36e36 Merge branch 'main' into fix/Sonarqube-Tech-Debt
SonarQube Analysis / sonarqube (pull_request) Successful in 3m49s
2026-05-20 13:03:20 -05:00
Thom LambandClaude Opus 4.7 2fae7738e3 test(pgsql): wrap CommandVisitor asserts in Assert.Multiple
Resolves SonarQube NUnit2045. The three independent parameter-index
assertions now report together instead of short-circuiting on the first
failure. The Is.Not.Null guard in the reflection helper stays outside the
multiple block because it gates the subsequent Invoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 13:02:28 -05:00
Thom LambandClaude Opus 4.7 5cb32d2311 refactor(query): extract shared Strata.SqlTools.Query model project
Moves the byte-identical 19-file ExpressionFactory/Query tree (duplicated
across Snowflake and SqlServer) into the new Strata.SqlTools.Query project
under the flat namespace Strata.SqlTools.Query. Both dialect projects now
reference the shared project; the Snowflake copies are deleted.

Also folds in the IDE0028 fix on CalculationFilterGroup.GetValidFilters
(collection expression []), which resolves both new-code IDE0028 smells
in one place now that there is a single copy.

Eliminates the 63 new duplicate lines flagged on the PR and removes the
largest contributor to the project's 11.1% duplication density. No
behavioral change: the moved types are identical to the originals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 12:04:01 -05:00
Thom LambandClaude Opus 4.7 c5a4a4b02b build(query): scaffold empty Strata.SqlTools.Query shared project
First step of extracting the duplicated ExpressionFactory/Query model
tree. Adds a BCL-only class library and registers it in the solution;
files are moved in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:13:44 -05:00
Thom LambandClaude Opus 4.7 70c9a3b3dc docs: implementation plan for Strata.SqlTools.Query extraction
Task-by-task plan to scaffold the shared project, move the 19 identical
ExpressionFactory/Query files, rewire dialect references, and clear the 3
remaining new-code SonarQube smells (IDE0028 x2, NUnit2045).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:05:59 -05:00
Thom LambandClaude Opus 4.7 55ab737888 docs: spec for Strata.SqlTools.Query shared-project extraction
Designs the extraction of the byte-identical 19-file ExpressionFactory/Query
tree (duplicated across Snowflake + SqlServer) into a new Strata.SqlTools.Query
project under a clean-break namespace, and folds in the 3 remaining new-code
SonarQube smells (IDE0028 x2 on CalculationFilterGroup, NUnit2045 on
CommandVisitorTests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:45:21 -05:00
Thom Lamb 30f451d80c feat(sonar): Add SonarQube static analysis and code coverage setup
SonarQube Analysis / sonarqube (pull_request) Successful in 5m37s
Establishes tooling to systematically analyze and address technical debt.

This includes:
- `scan-sonar.ps1`: An orchestration script for local SonarQube scans with coverage.
- `Directory.Build.props`: Integrates SonarAnalyzer.CSharp for static analysis during build.
- `coverlet.runsettings`: Configures code coverage collection using Coverlet.
- `.claude/settings.local.json`: Adds permissions for AI to query SonarQube and local dev status.
2026-05-19 17:17:41 -05:00
Thom LambandClaude Opus 4.7 e01cfae359 perf+docs: fix IsValid allocation regression and document FilterGroup asymmetry
Code review follow-up on 6f85358:

- CalculationFilterGroup.IsValid() previously called GetValidFilters().Any(),
  which materialized a full filtered List<CalculationFilter> just to check
  existence. Reimplement directly as Filters.Any(x => x.IsValid()) so
  IsValid() recovers its pre-refactor O(1) early-exit behavior without
  allocations. GetValidFilters() remains for callers that need the full
  materialized list.

- QueryConfigExtensions.GetAllColumnIds line 16 uses FilterGroup (not
  CalculationFilterGroup), which pre-filters in its JsonConstructor and
  has no GetValidFilters() method. Add a comment to document the
  intentional asymmetry between line 14 and line 16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:01:08 -05:00
Thom LambandClaude Opus 4.7 6f85358f63 refactor: address S2365 collection-copying property getters
Resolves SonarQube S2365 x3.

CalculationFilterGroup (Snowflake + SqlServer): the `Filters` getter
ran .Where(...).ToList() on every access. Convert to a plain
auto-property holding the raw collection plus a `GetValidFilters()`
method that does the filtering. Update internal IsValid() and the two
QueryConfigExtensions callers to use the new method.

HierarchicalData.AllChildData: part of the IHierarchicalData interface
and JSON-serialized; the transformation IS the property's contract.
Suppress S2365 with justification rather than refactor -- same pattern
as S3875.

Behavior change: CalculationFilterGroup JSON output now serializes the
raw filter collection rather than the pre-filtered one. Round-trip is
preserved; callers needing the filtered view must call GetValidFilters().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:55:13 -05:00
Thom LambandClaude Opus 4.7 f96161a664 test(pgsql): align CommandVisitorTests namespace and drop dead fallback
Code review follow-up on acfe29e:
- Switch namespace to Strata.SqlTools.SqlBreakdown.Tests.PostgreSql to
  match the dominant convention in the sibling test folder.
- Remove the unreachable SqlServer.CommandVisitor fallback in the
  reflection helper. The Assert.That guard alone is enough to surface
  a future regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:39:31 -05:00
Thom LambandClaude Opus 4.7 acfe29ee98 fix(pgsql): make CommandVisitor parameter index instance-scoped
Resolves SonarQube S2696 in CommandVisitor.cs.

The static _parameterIndex field was mutated from an instance method,
causing every new CommandVisitor to inherit the previous instance's
counter and never reset. Parameter indices now restart at $1 per
visitor, which is the intended PostgreSQL behavior.

Adds CommandVisitorTests.TwoVisitors_HaveIndependentParameterIndices to
lock in the contract via reflection (FormatParameterName is protected).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:21:17 -05:00
Thom LambandClaude Opus 4.7 e2bfe3906a fix(rules): suppress S3875 on intentional DSL operator==
Resolves SonarQube S3875 (BLOCKER) in Expression.Operators.cs.

The operator== returns a Comparison expression (DSL semantics), not a
bool. The existing CS0660/CS0661 pragma already documents this design;
the new SuppressMessage attribute makes the Sonar analyzer agree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:48:29 -05:00
Thom LambandClaude Opus 4.7 af2c3e054f fix(linq2sql): remove unread _breakdown field from builder
Resolves SonarQube S4487 in LinqQueryBreakdownBuilder.cs.

The field was assigned in the constructor but never read. Grep across
the project confirmed no external references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:42:38 -05:00
Thom LambandClaude Opus 4.7 986fdfc503 fix(rules): rename IVisitor.VisitNotEquals parameter to match impls
Resolves SonarQube S927 x2 in ExpressionVisitor.cs.

The interface parameter was named ``Equal`` on a ``VisitNotEquals`` method,
which is semantically wrong and forced implementations to mismatch.
Implementations already used ``notEqual``; rename the interface to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:19:10 -05:00
Thom LambandClaude Opus 4.7 41e98258bf docs: implementation plan for critical-tier SonarQube tech-debt sweep
Five-task plan implementing the 2026-05-19 spec: per-rule commits on the
existing fix/Sonarqube-Tech-Debt branch with build+test verification
between each. Notes one spec deviation discovered during plan-time
exploration (HierarchicalData.AllChildData is suppressed rather than
refactored — it's an interface contract member tied to JSON shape).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:58:24 -05:00
Thom LambandClaude Opus 4.7 6b461095c6 docs: spec for critical-tier SonarQube tech-debt sweep
Design doc covering the 8 BLOCKER/CRITICAL non-S3776 issues across six
projects. Approach is a pragmatic mix: fix where the rule reflects a
real defect, suppress with justification where the rule conflicts with
deliberate design (DSL operator==).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:24:24 -05:00
Thom Lamb 8b0368e810 test: fix SqlBreakdown tests project
SonarQube Analysis / sonarqube (push) Failing after 3m21s
2026-05-15 13:39:14 -05:00
Thom Lamb 5e467bcc9c chore: initial git load of code space 2026-05-12 08:52:33 -05:00