Skip to content

Comments

refactor(core): extract format sub-package from internal/mycli#503

Merged
apstndb merged 2 commits intomainfrom
extract-format-package
Feb 22, 2026
Merged

refactor(core): extract format sub-package from internal/mycli#503
apstndb merged 2 commits intomainfrom
extract-format-package

Conversation

@apstndb
Copy link
Owner

@apstndb apstndb commented Feb 22, 2026

Summary

Extract all formatter implementations from internal/mycli/ into a dedicated internal/mycli/format/ sub-package, breaking the type coupling between formatters and core types (*systemVariables, *Result, TableHeader).

This is Phase 3 of the internal/mycli/ restructuring (following #500 and #502).

Key Changes

  • format/config.go (new): Core types — Row (type alias), FormatConfig, FormatFunc, StreamingFormatter interface
  • format/format.go (moved from formatters.go): NewFormatter(), WriteTable(), WriteTableWithParams(), ExecuteWithFormatter(), NewStreamingFormatter(), TableParams
  • format/streaming_common.go (moved from formatters_common.go): CSVFormatter, TabFormatter, VerticalFormatter with updated signatures
  • format/streaming_table.go (moved from formatters_table_streaming.go): TableStreamingFormatter using FormatConfig instead of *systemVariables
  • format/streaming_html_xml.go (moved from formatters_html_xml_streaming.go): HTMLFormatter, XMLFormatter with updated signatures
  • format/sql.go (moved from formatters_sql.go): FormatSQL(), SQLStreamingFormatter, exported helper functions
  • format/width.go (new, extracted from cli_output.go): CalculateWidth() and all width calculation helpers (~240 lines)
  • system_variables.go: Added toFormatConfig() bridge method
  • row_processor.go: Updated to use format.FormatFunc and format.StreamingFormatter
  • cli_output.go: Removed ~200 lines of width calc code, added DisplayModeUnspecified handling for table formatting
  • formatter_utils.go: Reduced to only createStreamingProcessorForMode() thin wrapper

Development Insights

Discoveries

  • DisplayModeUnspecified (zero value) was not covered by the table-mode special case in printTableData, causing column alignment to be lost for EXPLAIN/PROFILE output. Fixed by including DisplayModeUnspecified in the table mode check.
  • Row type needed to be a type alias (type Row = format.Row) rather than a defined type to maintain seamless compatibility across the package boundary.
  • The VerboseHeaders field name (renamed from VerboseNames) better conveys that these are full header strings that may contain newlines between column name and type name (e.g., "Name\nSTRING").

Test Plan

  • make check passes (test + lint + fmt-check)
  • All existing tests pass including TestRenderPlanTree (column alignment preserved)
  • format/ package has zero imports from internal/mycli (no circular dependency)
  • go vet ./... clean

Fixes #498

Break type coupling between formatters and core types by introducing
FormatConfig struct, moving all formatter implementations to
internal/mycli/format/ sub-package with clean interface boundary.

Key changes:
- New FormatConfig replaces *systemVariables in formatter signatures
- StreamingFormatter interface takes []string column names instead of TableHeader
- FinishFormat() simplified to take no arguments
- Width calculation code extracted to format/width.go
- Row type alias (format.Row = []string) for zero-breaking-change migration
- format/ has zero imports from internal/mycli (no circular dependency)

Files moved (6): formatters.go, formatters_common.go,
formatters_table_streaming.go, formatters_html_xml_streaming.go,
formatters_sql.go, formatters_sql_test.go

New files (2): format/config.go, format/width.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @apstndb, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the core formatting logic by extracting it into a new, dedicated sub-package. This change aims to reduce tight coupling between formatters and core types, making the codebase more maintainable and testable. It centralizes formatting configurations and width calculation, streamlining how different output formats are handled throughout the application.

Highlights

  • New format sub-package: All formatter implementations have been extracted from internal/mycli/ into a dedicated internal/mycli/format/ sub-package to improve modularity and reduce coupling.
  • Decoupled FormatConfig: A new FormatConfig struct has been introduced to pass only the necessary configuration values to formatters, breaking the direct dependency on the *systemVariables type.
  • Centralized Width Calculation: The complex logic for calculating optimal column widths has been moved to format/width.go, making it reusable and isolated.
  • Refined TableHeader Interface: The TableHeader interface now uses a Render method, and the Row type has been aliased to format.Row for seamless compatibility across package boundaries.
  • Improved Table Display for Unspecified Mode: Fixed an issue where DisplayModeUnspecified was not correctly handled for table formatting, ensuring proper column alignment for EXPLAIN/PROFILE output.
Changelog
  • internal/mycli/cli_output.go
    • Removed various imports related to width calculation and iteration helpers, now handled by the new format package.
    • Updated renderTableHeader to use the new Render method on TableHeader.
    • Refactored printTableData to utilize format.FormatConfig and format.NewFormatter, including specific handling for DisplayModeUnspecified and table modes.
    • Removed all width calculation functions, as they were moved to format/width.go.
  • internal/mycli/cli_output_test.go
    • Removed io import and added internal/mycli/format import.
    • Updated testEmptyFormatter signature and calls to align with the new format.FormatFunc and format.FormatConfig types.
  • internal/mycli/execute_sql.go
    • Added internal/mycli/format import.
    • Updated extractTableNameFromQuery call to use the exported function from the format package.
  • internal/mycli/format/config.go
    • Added new file to define Row type alias, FormatConfig struct, FormatFunc type, and StreamingFormatter interface.
  • internal/mycli/format/format.go
    • Added new file, moving core formatter functions (writeBuffered, formatTable, WriteTable, NewFormatter, ExecuteWithFormatter, NewStreamingFormatter) from internal/mycli/formatters.go.
    • Updated function signatures to use format.FormatConfig and format.Row.
  • internal/mycli/format/sql.go
    • Renamed from internal/mycli/formatters_sql.go and updated package to format.
    • Exported extractTableNameFromQuery as ExtractTableNameFromQuery and parseSimpleTablePath as ParseSimpleTablePath.
    • Updated NewSQLStreamingFormatter and FormatSQL to accept FormatConfig instead of *systemVariables.
    • Modified SQLStreamingFormatter.InitFormat and FinishFormat signatures to align with the StreamingFormatter interface.
  • internal/mycli/format/sql_test.go
    • Renamed from internal/mycli/formatters_sql_test.go and updated package to format.
    • Updated test calls to use exported functions ParseSimpleTablePath and ExtractTableNameFromQuery.
  • internal/mycli/format/streaming_common.go
    • Renamed from internal/mycli/formatters_common.go and updated package to format.
    • Updated InitFormat and FinishFormat signatures for CSVFormatter, TabFormatter, and VerticalFormatter to use columnNames and FormatConfig.
  • internal/mycli/format/streaming_html_xml.go
    • Renamed from internal/mycli/formatters_html_xml_streaming.go and updated package to format.
    • Updated InitFormat and FinishFormat signatures for HTMLFormatter and XMLFormatter to use columnNames and FormatConfig.
  • internal/mycli/format/streaming_table.go
    • Renamed from internal/mycli/formatters_table_streaming.go and updated package to format.
    • Updated TableStreamingFormatter to use FormatConfig instead of *systemVariables.
    • Modified NewTableStreamingFormatter and InitFormat signatures to accept FormatConfig and columnNames.
    • Updated calculateWidths to use FormatConfig and the new CalculateWidth function from format/width.go.
  • internal/mycli/format/width.go
    • Added new file, moving all width calculation logic and helper functions from internal/mycli/cli_output.go.
  • internal/mycli/formatter_utils.go
    • Removed createStreamingFormatter and executeWithFormatter functions.
    • Updated createStreamingProcessorForMode to use sysVars.toFormatConfig() and call format.NewTableStreamingFormatter and format.NewStreamingFormatter.
  • internal/mycli/formatters.go
    • Removed file, as its contents were moved to internal/mycli/format/format.go.
  • internal/mycli/row_processor.go
    • Added internal/mycli/format import.
    • Updated BufferedProcessor and StreamingProcessor to use format.FormatFunc and format.StreamingFormatter.
    • Removed the local StreamingFormatter interface definition, now using the one from format/config.go.
    • Adjusted BufferedProcessor.Finish, TablePreviewProcessor.Finish, StreamingProcessor.Finish, TablePreviewProcessor.initializeFormatter, and StreamingProcessor.Init to align with the new format package interfaces and types.
  • internal/mycli/statement_processing.go
    • Added internal/mycli/format import.
    • Updated TableHeader interface to expose a Render method instead of internalRender.
    • Aliased Row type to format.Row for consistency.
  • internal/mycli/statements_dump.go
    • Added internal/mycli/format import.
    • Updated executeDumpBuffered to use session.systemVariables.toFormatConfig() and format.FormatSQL.
  • internal/mycli/streaming.go
    • Added internal/mycli/format import.
    • Updated isStreamingSupported to use format.NewStreamingFormatter and format.FormatConfig.
  • internal/mycli/streaming_test.go
    • Added internal/mycli/format import.
    • Updated TestBufferedVsStreaming to use sysVars.toFormatConfig(), format.NewFormatter, format.NewCSVFormatter, and formatter.FinishFormat() without arguments.
  • internal/mycli/system_variables.go
    • Added internal/mycli/format import.
    • Introduced toFormatConfig() method to convert systemVariables to format.FormatConfig.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Feb 22, 2026

Code Metrics Report

📊 View detailed coverage report (available for 7 days)

main (09d51fa) #503 (e257d5b) +/-
Coverage 70.0% 69.9% -0.1%
Code to Test Ratio 1:1.2 1:1.2 +0.0
Test Execution Time 1m12s 1m7s -5s
Details
  |                     | main (09d51fa) | #503 (e257d5b) |  +/-  |
  |---------------------|----------------|----------------|-------|
- | Coverage            |          70.0% |          69.9% | -0.1% |
  |   Files             |             54 |             55 |    +1 |
  |   Lines             |           5756 |           5712 |   -44 |
- |   Covered           |           4031 |           3997 |   -34 |
+ | Code to Test Ratio  |          1:1.2 |          1:1.2 |  +0.0 |
  |   Code              |          13178 |          13131 |   -47 |
+ |   Test              |          16492 |          16499 |    +7 |
+ | Test Execution Time |          1m12s |           1m7s |   -5s |

Code coverage of files in pull request scope (75.6% → 75.6%)

Files Coverage +/- Status
internal/mycli/cli_output.go 82.0% -6.0% modified
internal/mycli/execute_sql.go 73.1% 0.0% modified
internal/mycli/format/format.go 77.0% +77.0% added
internal/mycli/format/sql.go 87.0% +87.0% renamed
internal/mycli/format/streaming_common.go 73.0% +73.0% renamed
internal/mycli/format/streaming_html_xml.go 67.1% +67.1% renamed
internal/mycli/format/streaming_table.go 2.0% +2.0% renamed
internal/mycli/format/width.go 96.3% +96.3% added
internal/mycli/formatter_utils.go 81.8% +2.5% modified
internal/mycli/formatters.go 0.0% -84.8% removed
internal/mycli/formatters_common.go 0.0% -74.3% affected
internal/mycli/formatters_html_xml_streaming.go 0.0% -68.1% affected
internal/mycli/formatters_sql.go 0.0% -87.4% affected
internal/mycli/formatters_table_streaming.go 0.0% -1.7% affected
internal/mycli/row_processor.go 25.0% +1.3% modified
internal/mycli/statement_processing.go 84.1% 0.0% modified
internal/mycli/statements_dump.go 75.7% 0.0% modified
internal/mycli/streaming.go 79.3% 0.0% modified
internal/mycli/system_variables.go 94.9% +0.0% modified

Reported by octocov

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully refactors the formatting logic into a new internal/mycli/format package, improving architectural independence with FormatConfig. However, a critical security vulnerability exists in format.go where SQL comment display modes do not properly escape the */ sequence, potentially leading to SQL injection. Furthermore, the XML formatter's change to a streaming approach contradicts the repository's style guide, which mandates buffering for XML to ensure well-formed output.

Escape */ sequence in table content before wrapping in SQL comments
to prevent premature comment closure and potential SQL injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apstndb apstndb force-pushed the extract-format-package branch from 1ef87a5 to d725c90 Compare February 22, 2026 02:29
@apstndb
Copy link
Owner Author

apstndb commented Feb 22, 2026

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent and large-scale refactoring that successfully extracts the formatting logic into a dedicated internal/mycli/format sub-package. The changes effectively decouple the formatters from core mycli types like *systemVariables and *Result by introducing the format.FormatConfig struct and new FormatFunc and StreamingFormatter interfaces.

The code is well-organized, and the new package structure is much cleaner. The introduction of the systemVariables.toFormatConfig() bridge method is a clean way to manage the transition. The simplification of the StreamingFormatter.FinishFormat interface by removing unused parameters is a great improvement.

I've reviewed the changes in detail, including the extraction of width calculation logic and the updates to all formatters and their callers. The changes are consistent and well-executed. The test suite has been updated accordingly, which gives confidence that the refactoring has not introduced regressions.

I could not find any issues of high or critical severity. This is a high-quality contribution. Great work!

@apstndb
Copy link
Owner Author

apstndb commented Feb 22, 2026

/gemini summary

@gemini-code-assist
Copy link
Contributor

Summary of Changes

This pull request significantly refactors the application's core formatting logic by extracting it into a new, dedicated sub-package. This change aims to reduce tight coupling between various components, making the codebase more modular, maintainable, and testable. It centralizes formatting configurations and column width calculations, streamlining how different output formats are handled throughout the application while also addressing a critical security concern related to SQL comment handling.

Highlights

  • Dedicated Format Sub-package: All formatter implementations were extracted from internal/mycli/ into a new internal/mycli/format/ sub-package, significantly improving modularity and reducing coupling between formatters and core types.
  • Decoupled Configuration: A new FormatConfig struct was introduced to centralize and pass only the necessary configuration values to formatters, eliminating direct dependencies on the *systemVariables type.
  • Centralized Width Calculation: The complex logic for calculating optimal column widths was moved to format/width.go, making it reusable and isolated from cli_output.go.
  • Enhanced TableHeader Interface and Row Type: The TableHeader interface was updated to expose a Render method, and the Row type was aliased to format.Row to ensure seamless compatibility across package boundaries.
  • Improved Table Display for Unspecified Mode: An issue where DisplayModeUnspecified was not correctly handled for table formatting was fixed, ensuring proper column alignment for EXPLAIN/PROFILE output.
  • Security Vulnerability Fix: A potential SQL injection vulnerability in DisplayModeTableComment and DisplayModeTableDetailComment was addressed by sanitizing table content to prevent premature SQL comment closure.
Changelog
  • internal/mycli/cli_output.go
    • Removed various imports related to width calculation and iteration helpers, now handled by the new format package.
    • Updated renderTableHeader to use the new Render method on TableHeader.
    • Refactored printTableData to utilize format.FormatConfig and format.NewFormatter, including specific handling for DisplayModeUnspecified and table modes.
    • Removed all width calculation functions, as they were moved to format/width.go.
  • internal/mycli/cli_output_test.go
    • Removed io import and added internal/mycli/format import.
    • Updated testEmptyFormatter signature and calls to align with the new format.FormatFunc and format.FormatConfig types.
  • internal/mycli/execute_sql.go
    • Added internal/mycli/format import.
    • Updated extractTableNameFromQuery call to use the exported function from the format package.
  • internal/mycli/format/config.go
    • Added new file to define Row type alias, FormatConfig struct, FormatFunc type, and StreamingFormatter interface.
  • internal/mycli/format/format.go
    • Added new file, moving core formatter functions (writeBuffered, formatTable, WriteTable, NewFormatter, ExecuteWithFormatter, NewStreamingFormatter) from internal/mycli/formatters.go.
    • Updated function signatures to use format.FormatConfig and format.Row.
  • internal/mycli/format/sql.go
    • Renamed from internal/mycli/formatters_sql.go and updated package to format.
    • Exported extractTableNameFromQuery as ExtractTableNameFromQuery and parseSimpleTablePath as ParseSimpleTablePath.
    • Updated NewSQLStreamingFormatter and FormatSQL to accept FormatConfig instead of *systemVariables.
    • Modified SQLStreamingFormatter.InitFormat and FinishFormat signatures to align with the StreamingFormatter interface.
  • internal/mycli/format/sql_test.go
    • Renamed from internal/mycli/formatters_sql_test.go and updated package to format.
    • Updated test calls to use exported functions ParseSimpleTablePath and ExtractTableNameFromQuery.
  • internal/mycli/format/streaming_common.go
    • Renamed from internal/mycli/formatters_common.go and updated package to format.
    • Updated InitFormat and FinishFormat signatures for CSVFormatter, TabFormatter, and VerticalFormatter to use columnNames and FormatConfig.
  • internal/mycli/format/streaming_html_xml.go
    • Renamed from internal/mycli/formatters_html_xml_streaming.go and updated package to format.
    • Updated InitFormat and FinishFormat signatures for HTMLFormatter and XMLFormatter to use columnNames and FormatConfig.
  • internal/mycli/format/streaming_table.go
    • Renamed from internal/mycli/formatters_table_streaming.go and updated package to format.
    • Updated TableStreamingFormatter to use FormatConfig instead of *systemVariables.
    • Modified NewTableStreamingFormatter and InitFormat signatures to accept FormatConfig and columnNames.
    • Updated calculateWidths to use FormatConfig and the new CalculateWidth function from format/width.go.
  • internal/mycli/format/width.go
    • Added new file, moving all width calculation logic and helper functions from internal/mycli/cli_output.go.
  • internal/mycli/formatter_utils.go
    • Removed createStreamingFormatter and executeWithFormatter functions.
    • Updated createStreamingProcessorForMode to use sysVars.toFormatConfig() and call format.NewTableStreamingFormatter and format.NewStreamingFormatter.
  • internal/mycli/formatters.go
    • Removed file, as its contents were moved to internal/mycli/format/format.go.
  • internal/mycli/row_processor.go
    • Added internal/mycli/format import.
    • Updated BufferedProcessor and StreamingProcessor to use format.FormatFunc and format.StreamingFormatter.
    • Removed the local StreamingFormatter interface definition, now using the one from format/config.go.
    • Adjusted BufferedProcessor.Finish, TablePreviewProcessor.Finish, StreamingProcessor.Finish, TablePreviewProcessor.initializeFormatter, and StreamingProcessor.Init to align with the new format package interfaces and types.
  • internal/mycli/statement_processing.go
    • Added internal/mycli/format import.
    • Updated TableHeader interface to expose a Render method instead of internalRender.
    • Aliased Row type to format.Row for consistency.
  • internal/mycli/statements_dump.go
    • Added internal/mycli/format import.
    • Updated executeDumpBuffered to use session.systemVariables.toFormatConfig() and format.FormatSQL.
  • internal/mycli/streaming.go
    • Added internal/mycli/format import.
    • Updated isStreamingSupported to use format.NewStreamingFormatter and format.FormatConfig.
  • internal/mycli/system_variables.go
    • Added internal/mycli/format import.
    • Introduced toFormatConfig() method to convert systemVariables to format.FormatConfig.
Activity
  • An initial summary of changes was provided by gemini-code-assist[bot], highlighting the refactoring into a new sub-package, decoupled configuration, centralized width calculation, and improved table display.
  • A code metrics report was posted by github-actions[bot], indicating a slight decrease in overall coverage by 0.1% and a 5-second reduction in test execution time.
  • gemini-code-assist[bot] identified a high-priority security vulnerability related to SQL injection in DisplayModeTableComment and DisplayModeTableDetailComment due to unsanitized table content.
  • The author, apstndb, acknowledged the security feedback and confirmed the fix in commit 1ef87a5.

@apstndb apstndb merged commit d333001 into main Feb 22, 2026
2 checks passed
@apstndb apstndb deleted the extract-format-package branch February 22, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 3: Extract formatters via interface boundary

1 participant