Skip to content

Add SSSQL list/remove authoring flow and reference docs#765

Merged
mk3008 merged 2 commits intomainfrom
764-sssql-cli拡張-removelist追加existsnot-exists-scaffold対応安全なidempotent-authoring
Apr 14, 2026

Hidden character warning

The head ref may contain hidden characters: "764-sssql-cli\u62e1\u5f35-removelist\u8ffd\u52a0existsnot-exists-scaffold\u5bfe\u5fdc\u5b89\u5168\u306aidempotent-authoring"
Merged

Add SSSQL list/remove authoring flow and reference docs#765
mk3008 merged 2 commits intomainfrom
764-sssql-cli拡張-removelist追加existsnot-exists-scaffold対応安全なidempotent-authoring

Conversation

@mk3008
Copy link
Copy Markdown
Owner

@mk3008 mk3008 commented Apr 13, 2026

Issue

Customer Value

  • Developers can now inspect, author, refresh, remove, and bulk-remove recognized SSSQL branches from the CLI instead of editing optional-condition SQL by hand.
  • The SSSQL workflow is easier to understand because the command surface and runtime pruning contract now have a single reader-facing reference page.

Outcome

  • Added ztd query sssql list to report recognized SSSQL branches in text and JSON forms.
  • Added structured ztd query sssql scaffold support for scalar operators plus EXISTS / NOT EXISTS authoring with preview mode, idempotency, and fail-fast validation.
  • Added ztd query sssql remove for single-branch removal and ztd query sssql remove --all for bulk removal of all recognized branches.
  • Kept refresh focused on re-anchoring existing recognized scalar branches without inventing new runtime predicates.
  • Updated docs and routing guidance so README stays an entry-point index while SSSQL behavior lives in dedicated guides.

Acceptance Criteria

  • ztd query sssql list <sqlFile> reports recognized authored branches in machine-readable and human-readable forms.
  • ztd query sssql remove <sqlFile> removes exactly one targeted recognized branch safely, supports preview, and stays idempotent when the target is already absent.
  • ztd query sssql remove <sqlFile> --all removes every recognized branch explicitly and rejects mixed targeted flags.
  • ztd query sssql scaffold <sqlFile> supports richer scalar operators and structured EXISTS / NOT EXISTS authoring.
  • Scaffold, refresh, and remove flows remain idempotent and fail fast on ambiguous or unsafe input.
  • Runtime pruning still depends on explicit optionalConditionParameters and supports authored scalar, EXISTS, and NOT EXISTS branches.
  • Docs describe the current CLI/API surface and routing behavior accurately.

Verification

  • pnpm exec vitest run packages/core/tests/transformers/SSSQLFilterBuilder.test.ts
  • pnpm exec vitest run packages/ztd-cli/tests/cliCommands.test.ts -t "query sssql"
  • pnpm --filter @rawsql-ts/ztd-cli build

Repository Evidence

  • Core SSSQL authoring and removal helpers: packages/core/src/transformers/SSSQLFilterBuilder.ts
  • Focused pruning and builder coverage: packages/core/tests/transformers/SSSQLFilterBuilder.test.ts
  • CLI surface and rewrite reporting: packages/ztd-cli/src/commands/query.ts
  • CLI behavior coverage: packages/ztd-cli/tests/cliCommands.test.ts
  • Reader-facing docs: docs/guide/ztd-cli-sssql-reference.md, docs/guide/ztd-cli-sssql-authoring.md, packages/ztd-cli/README.md
  • Changeset: .changeset/sssql-cli-reference-and-remove-all.md

Supplementary Evidence

  • DB-dependent CLI tests were skipped locally because pg_dump is not available in PATH.
  • I did not run a full docs site build in this branch.

Open Questions

  • The current refresh implementation still keys off recognized scalar branch targets. I did not extend it to infer and move correlated EXISTS / NOT EXISTS branches because that behavior is not yet safely proven.

Merge Blockers

  • No known code blockers.
  • Reviewer should confirm the changeset release type is correct for rawsql-ts and @rawsql-ts/ztd-cli.

Merge Readiness

  • No baseline exception requested.
  • Baseline exception requested and linked below.

CLI Surface Migration

  • No migration packet required for this CLI change.
  • CLI/user-facing surface change and migration packet completed.
    Upgrade note: ztd query sssql now includes list, structured scaffold, targeted remove, and remove --all; non-preview rewrites overwrite the source SQL file by default unless --out is supplied.
    Deprecation/removal plan or issue: No deprecation is introduced in this PR.
    Docs/help/examples updated: Added docs/guide/ztd-cli-sssql-reference.md and updated docs/guide/ztd-cli-sssql-authoring.md plus packages/ztd-cli/README.md.
    Release/changeset wording: Covered by .changeset/sssql-cli-reference-and-remove-all.md.

Scaffold Contract Proof

  • No scaffold contract proof required for this PR.
  • Scaffold contract proof completed.
    Non-edit assertion: query sssql list inspects recognized branches without mutating the SQL file.
    Fail-fast input-contract proof: Added coverage for empty anchorColumns and preserved fail-fast handling for unsupported scalar operator parsing.
    Generated-output viability proof: Focused CLI tests verify preview/apply flows, overwrite-by-default behavior, branch removal, and bulk removal.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR adds CLI inspection/removal for SSSQL (list, remove, remove --all), expands scaffold support to scalar operators and structured EXISTS/NOT EXISTS branches, implements branch introspection and safe rewrite/reporting (including comment-loss checks), updates core branch APIs, and refreshes docs and tests to match runtime pruning semantics.

Changes

Cohort / File(s) Summary
Release & Changeset
/.changeset/sssql-cli-reference-and-remove-all.md
Adds a changeset documenting CLI additions (list/remove/remove --all), scalar operator expansion, structured EXISTS/NOT EXISTS scaffolding, comment-preservation safeguard, and exports for branch metadata/removal helpers.
Documentation: Conceptual & Routing
docs/dogfooding/dynamic-filter-routing.md, docs/guide/dynamic-filter-routing.md, docs/guide/sssql-optional-branch-pruning.md, docs/guide/ztd-cli-sssql-authoring.md
Reframes routing guidance to prefer binding existing placeholders or authoring SSSQL branches (not runtime injection), documents NOT EXISTS and expanded scalar operator coverage, and integrates CLI inspection/removal into authoring workflows.
Documentation: Index & Reference
docs/guide/feature-index.md, docs/guide/ztd-cli-sssql-reference.md, packages/ztd-cli/README.md
Adds Feature Index entry and a comprehensive ztd query sssql reference page; updates CLI README and links to the new reference.
Core Logic: Branch Handling
packages/core/src/transformers/SSSQLFilterBuilder.ts
Introduces public types and APIs for branch inspection and mutation (SssqlBranchKind, SssqlBranchInfo, scaffold/remove specs), adds list(), scaffoldBranch(), scaffold(), remove() and removeAll(), generalizes scalar operator scaffolding, implements EXISTS/NOT EXISTS scaffold validation, SQL normalization and pattern extraction.
Core Tests: Pruning & Scaffold
packages/core/tests/transformers/PruneOptionalConditionBranches.test.ts, packages/core/tests/transformers/SSSQLFilterBuilder.test.ts
Adds tests for NOT EXISTS pruning and extensive SSSQLFilterBuilder coverage: idempotency, operator normalization (!=<>), exists/not-exists scaffolding, list() metadata, remove()/removeAll() behaviors, and invalid-anchor failure cases.
CLI Implementation & Tests
packages/ztd-cli/src/commands/query.ts, packages/ztd-cli/tests/cliCommands.test.ts
Adds query sssql list and query sssql remove subcommands; extends scaffold/refresh with structured flags and --preview; introduces rewrite pipeline with parsing/formatting/comment-loss detection and unified diff/json reporting; adds comprehensive CLI tests for new flows.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI as ztd-cli
  participant Core as rawsql-ts.SSSQLFilterBuilder
  participant FS as FileSystem
  participant Diff as Diff/Formatter

  User->>CLI: "ztd query sssql scaffold/remove/list <sqlFile> --flags"
  CLI->>FS: read <sqlFile>
  CLI->>Core: list/scaffoldBranch/remove/removeAll (parsed SQL, spec)
  Core-->>CLI: branch info / rewritten SQL / errors
  CLI->>Diff: format & create diff (if --preview)
  Diff-->>CLI: unified diff / formatted SQL
  CLI->>FS: write file (unless --preview)
  CLI-->>User: text/json report (diff, changed/written, branches)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hop through branches, tidy and neat,
I list each param and prune what we don't need.
EXISTS nods, NOT EXISTS shyly hides,
Comments kept safe where the old SQL resides.
A nibble, a tweak — author, inspect, succeed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding SSSQL list/remove authoring flow and reference documentation.
Linked Issues check ✅ Passed The PR implements all major objectives from issue #764: list/remove commands, scalar operator support, EXISTS/NOT EXISTS scaffolding, idempotency, fail-fast validation, runtime pruning clarity, and docs updates.
Out of Scope Changes check ✅ Passed All changes align with issue #764 scope: SSSQL CLI enhancements, documentation updates, core builder changes, and tests support the stated objectives with no unrelated modifications detected.
Description check ✅ Passed PR description comprehensively addresses all required template sections including summary, verification, merge readiness, CLI surface migration, and scaffold contract proof with checkmarks and detailed explanations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 764-sssql-cli拡張-removelist追加existsnot-exists-scaffold対応安全なidempotent-authoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mk3008 mk3008 marked this pull request as ready for review April 14, 2026 00:43
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
packages/core/tests/transformers/SSSQLFilterBuilder.test.ts (1)

148-226: Add removal coverage for not-exists branches.

This file now scaffolds the new kind, but the removal assertions here still only exercise exists. A regression in list/remove/removeAll handling for not-exists would slip through.

🧪 Example test to close the gap
+    it('removes not-exists branches idempotently', () => {
+        const builder = new SSSQLFilterBuilder();
+        const scaffolded = builder.scaffoldBranch(
+            `
+                SELECT p.product_id, p.product_name
+                FROM products p
+            `,
+            {
+                kind: 'not-exists',
+                parameterName: 'archived_name',
+                anchorColumns: ['products.product_id'],
+                query: `
+                    SELECT 1
+                    FROM archived_products ap
+                    WHERE ap.product_id = $c0
+                      AND ap.product_name = :archived_name
+                `
+            }
+        );
+
+        const removed = builder.remove(scaffolded, { parameterName: 'archived_name', kind: 'not-exists' });
+        expect(normalizeSql(new SqlFormatter().format(removed).formattedSql))
+            .toBe('select "p"."product_id", "p"."product_name" from "products" as "p"');
+
+        expect(normalizeSql(new SqlFormatter().format(builder.removeAll(scaffolded)).formattedSql))
+            .toBe('select "p"."product_id", "p"."product_name" from "products" as "p"');
+    });
As per coding guidelines, "Unless the request explicitly says not to, behavior changes must add or update tests in the same change" and "When ambiguous, prefer the strongest executable test for the changed behavior."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/tests/transformers/SSSQLFilterBuilder.test.ts` around lines 148
- 226, Add coverage for the new "not-exists" branch kind in the
SSSQLFilterBuilder tests: in the existing tests that scaffold an "exists" branch
(using SSSQLFilterBuilder.scaffoldBranch), also scaffold a parallel branch with
{ kind: 'not-exists', parameterName: 'category_name', anchorColumns: [...],
query: '...' } and update assertions for builder.list(...) to expect an entry
with parameterName 'category_name' and kind 'not-exists'; then mirror the
remove/removeAll assertions to call builder.remove(...) and
builder.removeAll(...) for the not-exists branch and assert that the normalized
SQL no longer contains the :category_name placeholder and that repeated remove
is idempotent (same normalized SQL), and that removeAll yields the original base
SELECT without the not-exists branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/guide/ztd-cli-sssql-authoring.md`:
- Around line 51-54: The docs example uses the wrong flag name: replace the
incorrect --parameter flag in the example command ("ztd query sssql remove
src/sql/products/list_products.sql --parameter category_name --preview") with
the actual CLI flag --parameter-name so it matches the CLI implementation (use
--parameter-name category_name); update the sample line(s) accordingly to ensure
the example matches the defined flag.

In `@packages/core/src/transformers/SSSQLFilterBuilder.ts`:
- Around line 565-579: In scaffoldExistsBranch, add a fail-fast validation that
spec.anchorColumns is non-empty before resolving targets: check
spec.anchorColumns.length and if zero throw a clear Error (e.g., "SSSQL
EXISTS/NOT EXISTS scaffold requires at least one anchorColumn.") so you never
dereference targetQueries[0] or call targetQuery.appendWhere; update logic
around resolveTarget(root, anchorColumn), anchorTargets, and targetQueries
accordingly to assume at least one anchor column is present.
- Around line 297-331: In getScalarBranchDetails, avoid calling
normalizeScalarOperator(predicate.operator...) before verifying the binary
predicate shape and parameter/column positions; instead, first ensure predicate
is a BinaryExpression and that one side is a ParameterExpression matching
parameterName and the other is a ColumnReference (using the existing left/right
instanceof checks), then call normalizeScalarOperator and validate the operator;
if the operator is unrecognized or normalization would fail, return null so the
caller falls back to the generic "expression" handling rather than throwing;
reference getScalarBranchDetails, predicate.operator, BinaryExpression,
normalizeScalarOperator, ParameterExpression, and ColumnReference when making
the change.

In `@packages/ztd-cli/src/commands/query.ts`:
- Around line 925-948: The current logic never overwrites the input file unless
options.out is provided, but the intended behavior is that non-preview runs
should default to overwriting the original SQL file; update the flow so that
when options.out is falsy and preview is false you treat outputFile as the input
path (i.e. set outputFile = absoluteInputPath or write to absoluteInputPath),
then call writeFileSync with that resolved path and ensure the returned fields
(output_file and written) reflect this (update references to outputFile, created
diff via createTwoFilesPatch stays correct using outputFile ??
absoluteInputPath). Use the existing symbols outputFile, preview,
absoluteInputPath, writeFileSync and the returned written/output_file flags to
implement this single-path overwrite behavior.
- Around line 653-657: collectSupportedOptionalConditionBranches() returns only
parameterName but SSSQLFilterBuilder.refresh() expects keys to map to target
column names (and anchor info) so branches like --parameter q or EXISTS branches
(e.g., :category_name on p.product_id) cannot be re-anchored and throw “Could
not resolve SSSQL filter target ...”. Fix by changing the transform to pass a
map keyed by the filter's actual target/anchor metadata instead of just
parameterName: call collectSupportedOptionalConditionBranches (or extend it) to
include each branch's targetColumn and anchorContext and construct filters as
entries using that target/anchor identifier (preserving parameterName as
value/null), then call new SSSQLFilterBuilder().refresh(parsed, filters) so
refresh can resolve and re-anchor branches correctly (refer to
collectSupportedOptionalConditionBranches, SSSQLFilterBuilder.refresh,
parameterName, targetColumn, anchorContext).

---

Nitpick comments:
In `@packages/core/tests/transformers/SSSQLFilterBuilder.test.ts`:
- Around line 148-226: Add coverage for the new "not-exists" branch kind in the
SSSQLFilterBuilder tests: in the existing tests that scaffold an "exists" branch
(using SSSQLFilterBuilder.scaffoldBranch), also scaffold a parallel branch with
{ kind: 'not-exists', parameterName: 'category_name', anchorColumns: [...],
query: '...' } and update assertions for builder.list(...) to expect an entry
with parameterName 'category_name' and kind 'not-exists'; then mirror the
remove/removeAll assertions to call builder.remove(...) and
builder.removeAll(...) for the not-exists branch and assert that the normalized
SQL no longer contains the :category_name placeholder and that repeated remove
is idempotent (same normalized SQL), and that removeAll yields the original base
SELECT without the not-exists branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b7b8253-156d-4bca-af1e-4c9eee981bb4

📥 Commits

Reviewing files that changed from the base of the PR and between 011df14 and 6a1cb41.

📒 Files selected for processing (13)
  • .changeset/sssql-cli-reference-and-remove-all.md
  • docs/dogfooding/dynamic-filter-routing.md
  • docs/guide/dynamic-filter-routing.md
  • docs/guide/feature-index.md
  • docs/guide/sssql-optional-branch-pruning.md
  • docs/guide/ztd-cli-sssql-authoring.md
  • docs/guide/ztd-cli-sssql-reference.md
  • packages/core/src/transformers/SSSQLFilterBuilder.ts
  • packages/core/tests/transformers/PruneOptionalConditionBranches.test.ts
  • packages/core/tests/transformers/SSSQLFilterBuilder.test.ts
  • packages/ztd-cli/README.md
  • packages/ztd-cli/src/commands/query.ts
  • packages/ztd-cli/tests/cliCommands.test.ts

Comment on lines +51 to +54
```bash
ztd query sssql list src/sql/products/list_products.sql
ztd query sssql remove src/sql/products/list_products.sql --parameter category_name --preview
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the actual remove flag name in this example.

Line 53 uses --parameter, but packages/ztd-cli/src/commands/query.ts defines --parameter-name, so the sample command will fail as written.

📝 Suggested doc fix
 ztd query sssql list src/sql/products/list_products.sql
-ztd query sssql remove src/sql/products/list_products.sql --parameter category_name --preview
+ztd query sssql remove src/sql/products/list_products.sql --parameter-name category_name --preview
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
ztd query sssql list src/sql/products/list_products.sql
ztd query sssql remove src/sql/products/list_products.sql --parameter category_name --preview
```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/guide/ztd-cli-sssql-authoring.md` around lines 51 - 54, The docs example
uses the wrong flag name: replace the incorrect --parameter flag in the example
command ("ztd query sssql remove src/sql/products/list_products.sql --parameter
category_name --preview") with the actual CLI flag --parameter-name so it
matches the CLI implementation (use --parameter-name category_name); update the
sample line(s) accordingly to ensure the example matches the defined flag.

Comment thread packages/core/src/transformers/SSSQLFilterBuilder.ts
Comment thread packages/core/src/transformers/SSSQLFilterBuilder.ts
Comment on lines +653 to +657
transform: (sql) => {
const parsed = SelectQueryParser.parse(sql);
const existingBranches = collectSupportedOptionalConditionBranches(parsed);
const filters = Object.fromEntries(existingBranches.map((branch) => [branch.parameterName, null]));
return new SSSQLFilterBuilder().refresh(parsed, filters);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Refresh is keyed by parameter names, so structured branches stop being refreshable.

collectSupportedOptionalConditionBranches() only gives you parameterName, but SSSQLFilterBuilder.refresh() resolves each key as a target column name. That works for :status -> status, but it breaks as soon as the scaffold uses --parameter q or an EXISTS branch like :category_name anchored on p.product_id; those now fail with “Could not resolve SSSQL filter target ...” instead of re-anchoring the branch. refresh needs target/anchor metadata here, not just parameterName.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ztd-cli/src/commands/query.ts` around lines 653 - 657,
collectSupportedOptionalConditionBranches() returns only parameterName but
SSSQLFilterBuilder.refresh() expects keys to map to target column names (and
anchor info) so branches like --parameter q or EXISTS branches (e.g.,
:category_name on p.product_id) cannot be re-anchored and throw “Could not
resolve SSSQL filter target ...”. Fix by changing the transform to pass a map
keyed by the filter's actual target/anchor metadata instead of just
parameterName: call collectSupportedOptionalConditionBranches (or extend it) to
include each branch's targetColumn and anchorContext and construct filters as
entries using that target/anchor identifier (preserving parameterName as
value/null), then call new SSSQLFilterBuilder().refresh(parsed, filters) so
refresh can resolve and re-anchor branches correctly (refer to
collectSupportedOptionalConditionBranches, SSSQLFilterBuilder.refresh,
parameterName, targetColumn, anchorContext).

Comment thread packages/ztd-cli/src/commands/query.ts Outdated
@mk3008 mk3008 merged commit d1a4fb3 into main Apr 14, 2026
10 of 13 checks passed
@mk3008 mk3008 deleted the 764-sssql-cli拡張-removelist追加existsnot-exists-scaffold対応安全なidempotent-authoring branch April 14, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSSQL CLI拡張: remove/list追加、EXISTS/NOT EXISTS scaffold対応、安全なidempotent authoring

1 participant