Skip to content

Commit d437540

Browse files
ajitpratap0Ajit Pratap Singhclaude
authored
docs: fix pkg.go.dev license detection and comprehensive doc improvements (v1.9.1) (#351)
PRIMARY FIX: Remove leading blank line from LICENSE file that prevented google/licensecheck from detecting Apache-2.0, causing ALL documentation to be suppressed as non-redistributable on pkg.go.dev. DOCUMENTATION IMPROVEMENTS (52 files): - Consolidate competing package doc comments: each package now has exactly one authoritative doc comment in doc.go; secondary files converted to file-level comments (blank line before package declaration) - Add missing package-level doc comments to formatter, transform, schema, advisor, cbinding packages - Add doc comments to all exported symbols in models, ast, errors, transform - Add individual doc comments to all 100+ TokenType constants with SQL examples - Add comprehensive docs to all 24 error builder functions - Add docs to all exported transform functions (AddWhere, ReplaceWhere, etc.) - Add docs to all exported AST node methods (TokenLiteral, Children, SQL) - Expand DefaultFormatOptions, Optimizer, and C-binding package docs Resolves: pkg.go.dev showing UNKNOWN license and suppressing all documentation Effect: After v1.9.1 tag push, pkg.go.dev will re-crawl and show Apache-2.0 Co-authored-by: Ajit Pratap Singh <ajitpratapsingh@Ajits-Mac-mini-2655.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ecb9ae0 commit d437540

53 files changed

Lines changed: 2199 additions & 654 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

LICENSE

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
Apache License
32
Version 2.0, January 2004
43
http://www.apache.org/licenses/

pkg/advisor/optimizer.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,34 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
// Package advisor provides SQL query optimization suggestions by analyzing parsed ASTs.
15+
// Package advisor provides SQL query optimization analysis by walking parsed ASTs and
16+
// applying configurable rules that detect common performance anti-patterns.
1617
//
17-
// The optimizer walks the Abstract Syntax Tree produced by the GoSQLX parser and
18-
// applies configurable rules to detect common performance anti-patterns. Each rule
19-
// produces zero or more Suggestions with severity levels, human-readable messages,
20-
// and (where possible) suggested SQL rewrites.
18+
// The central type is Optimizer, created with New() (all built-in rules) or
19+
// NewWithRules(...Rule) for a custom rule set. Optimizer.AnalyzeSQL is a convenience
20+
// method that parses a SQL string and returns an OptimizationResult containing a slice
21+
// of Suggestion values, a query complexity classification (simple / moderate / complex),
22+
// and an optimization score from 0 (worst) to 100 (no issues). Each Suggestion carries
23+
// a rule ID, severity (info / warning / error), a human-readable message and detail, the
24+
// source location, and where possible a suggested SQL rewrite.
25+
//
26+
// Eight built-in rules are registered by DefaultRules:
27+
//
28+
// OPT-001 SELECT * Detection — recommend listing columns explicitly
29+
// OPT-002 Missing WHERE Clause — UPDATE/DELETE without WHERE affects all rows
30+
// OPT-003 Cartesian Product — implicit cross join from multiple FROM tables
31+
// OPT-004 SELECT DISTINCT Overuse — DISTINCT may mask incorrect join conditions
32+
// OPT-005 Subquery in WHERE — suggest converting correlated subqueries to JOINs
33+
// OPT-006 OR in WHERE Clause — OR on different columns may prevent index usage
34+
// OPT-007 Leading Wildcard in LIKE — LIKE '%...' forces a full table scan
35+
// OPT-008 Function on Indexed Column — wrapping a column in a function defeats B-tree indexes
36+
//
37+
// Custom rules implement the Rule interface (ID, Name, Description, Analyze) and are
38+
// passed to NewWithRules. All built-in rules are stateless and safe for concurrent use.
2139
//
2240
// Quick Start:
2341
//
24-
// opt := optimizer.New()
42+
// opt := advisor.New()
2543
// result, err := opt.AnalyzeSQL("SELECT * FROM users")
2644
// if err != nil {
2745
// log.Fatal(err)

pkg/cbinding/cbinding.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,31 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
// Package main is compiled as a C-shared library (libgosqlx) that exposes the GoSQLX
16+
// SQL parsing engine to non-Go languages via a plain C FFI.
17+
//
18+
// The library is built with "go build -buildmode=c-shared" (see build.sh) and produces
19+
// a platform-native shared object — libgosqlx.so on Linux, libgosqlx.dylib on macOS,
20+
// and libgosqlx.dll on Windows — together with a generated C header (libgosqlx.h).
21+
// The primary consumer is the Python package pygosqlx, which loads the library at
22+
// runtime via ctypes, but any language with a C FFI (Ruby, Node.js via N-API, Rust FFI,
23+
// etc.) can call the exported symbols directly.
24+
//
25+
// All exported functions follow the same contract: they accept C strings (null-terminated
26+
// UTF-8) and return a newly allocated C string containing a JSON-encoded result object.
27+
// Callers must free every returned string with gosqlx_free to avoid memory leaks.
28+
//
29+
// Exported symbols:
30+
//
31+
// gosqlx_parse(sql) — parse SQL, return ParseResult JSON
32+
// gosqlx_validate(sql) — validate SQL syntax, return ValidationResult JSON
33+
// gosqlx_format(sql) — format SQL, return FormatResult JSON
34+
// gosqlx_extract_tables(sql) — extract referenced table names as JSON array
35+
// gosqlx_extract_columns(sql) — extract referenced column names as JSON array
36+
// gosqlx_extract_functions(sql) — extract referenced function names as JSON array
37+
// gosqlx_extract_metadata(sql) — extract tables, columns, functions with schema qualification
38+
// gosqlx_version() — return the library version string (e.g. "1.9.0")
39+
// gosqlx_free(ptr) — free a string previously returned by any gosqlx_* function
1540
package main
1641

1742
// #include <stdlib.h>

pkg/errors/builders.go

Lines changed: 203 additions & 24 deletions
Large diffs are not rendered by default.

pkg/errors/cache.go

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,19 +109,31 @@ func (c *keywordSuggestionCache) size() int {
109109
return len(c.cache)
110110
}
111111

112-
// ClearSuggestionCache clears the keyword suggestion cache.
113-
// Useful for testing or when keyword list changes.
112+
// ClearSuggestionCache removes all entries from the keyword suggestion cache.
113+
// Call this in tests to ensure a clean state between test cases, or after
114+
// modifying the keyword list so that stale suggestions are not served.
114115
func ClearSuggestionCache() {
115116
suggestionCache.clear()
116117
}
117118

118-
// SuggestionCacheSize returns the current size of the suggestion cache.
119-
// Useful for monitoring and debugging.
119+
// SuggestionCacheSize returns the number of entries currently held in the keyword
120+
// suggestion cache. Use this for monitoring cache growth and deciding whether to
121+
// adjust the maximum size.
120122
func SuggestionCacheSize() int {
121123
return suggestionCache.size()
122124
}
123125

124-
// SuggestionCacheStats returns cache statistics
126+
// SuggestionCacheStats holds observability metrics for the keyword suggestion cache.
127+
// Retrieve an instance via GetSuggestionCacheStats and reset counters via
128+
// ResetSuggestionCacheStats.
129+
//
130+
// Fields:
131+
// - Size: Current number of entries in the cache
132+
// - MaxSize: Configured maximum capacity (default 1000)
133+
// - Hits: Number of cache lookups that returned a cached value
134+
// - Misses: Number of cache lookups that required computing a new suggestion
135+
// - Evictions: Total number of entries removed during partial eviction sweeps
136+
// - HitRate: Ratio of Hits to (Hits + Misses); 0.0 when no lookups have occurred
125137
type SuggestionCacheStats struct {
126138
Size int
127139
MaxSize int
@@ -131,7 +143,14 @@ type SuggestionCacheStats struct {
131143
HitRate float64
132144
}
133145

134-
// GetSuggestionCacheStats returns current cache statistics
146+
// GetSuggestionCacheStats returns a snapshot of the current keyword suggestion cache
147+
// metrics. The returned struct is safe to read without any additional locking.
148+
// Use this in observability dashboards or benchmarks to track cache efficiency.
149+
//
150+
// Example:
151+
//
152+
// stats := errors.GetSuggestionCacheStats()
153+
// fmt.Printf("Cache hit rate: %.1f%%\n", stats.HitRate*100)
135154
func GetSuggestionCacheStats() SuggestionCacheStats {
136155
hits := atomic.LoadUint64(&suggestionCache.hits)
137156
misses := atomic.LoadUint64(&suggestionCache.misses)
@@ -153,8 +172,9 @@ func GetSuggestionCacheStats() SuggestionCacheStats {
153172
}
154173
}
155174

156-
// ResetSuggestionCacheStats resets the cache statistics counters.
157-
// Useful for testing and monitoring.
175+
// ResetSuggestionCacheStats zeroes all hit, miss, and eviction counters in the
176+
// keyword suggestion cache without clearing cached entries. Call this at the start
177+
// of a benchmark or monitoring interval to obtain a clean measurement window.
158178
func ResetSuggestionCacheStats() {
159179
atomic.StoreUint64(&suggestionCache.hits, 0)
160180
atomic.StoreUint64(&suggestionCache.misses, 0)

pkg/errors/formatter.go

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@ import (
2121
"github.com/ajitpratap0/GoSQLX/pkg/models"
2222
)
2323

24-
// FormatErrorWithContext formats an error with SQL context and visual indicators
25-
// This is a convenience function that wraps the Error.Error() method
24+
// FormatErrorWithContext formats an error with SQL context and visual indicators.
25+
// For *Error values it delegates to the structured Error.Error() method, which
26+
// includes code, location, SQL context highlighting, hint, and help URL. For all
27+
// other error types it falls back to a plain "Error: <message>" string.
28+
//
29+
// Parameters:
30+
// - err: The error to format (may be *Error or a generic error)
31+
// - sql: The SQL source; currently unused for *Error (context is already attached)
32+
//
33+
// Returns the formatted error string ready for display to end users.
2634
func FormatErrorWithContext(err error, sql string) string {
2735
// If it's already a structured error, just return its formatted string
2836
if structErr, ok := err.(*Error); ok {
@@ -33,7 +41,19 @@ func FormatErrorWithContext(err error, sql string) string {
3341
return fmt.Sprintf("Error: %v", err)
3442
}
3543

36-
// FormatErrorWithContextAt formats an error at a specific location with SQL context
44+
// FormatErrorWithContextAt creates a structured error for the given code and location,
45+
// attaches the SQL context window, and auto-generates a hint. It then returns the
46+
// fully-formatted error string. This is useful for one-shot error formatting without
47+
// retaining the *Error value.
48+
//
49+
// Parameters:
50+
// - code: ErrorCode classifying the error category (e.g., ErrCodeExpectedToken)
51+
// - message: Human-readable description of the error
52+
// - location: Precise line/column where the error occurred
53+
// - sql: Full SQL source used to generate the context window
54+
// - highlightLen: Number of characters to highlight at the error column
55+
//
56+
// Returns the complete formatted error string including context highlighting.
3757
func FormatErrorWithContextAt(code ErrorCode, message string, location models.Location, sql string, highlightLen int) string {
3858
err := NewError(code, message, location)
3959
err = err.WithContext(sql, highlightLen)
@@ -46,8 +66,17 @@ func FormatErrorWithContextAt(code ErrorCode, message string, location models.Lo
4666
return err.Error()
4767
}
4868

49-
// FormatMultiLineContext formats error context for multi-line SQL with extended context
50-
// Shows up to 3 lines (1 before, error line, 1 after) with proper indentation
69+
// FormatMultiLineContext formats an SQL context window around a specific error location.
70+
// It shows up to three lines: one before the error, the error line itself, and one
71+
// after. A caret indicator (^) is rendered below the error column, with optional
72+
// multi-character highlighting when highlightLen > 1.
73+
//
74+
// Parameters:
75+
// - sql: The full SQL source string (may contain newlines)
76+
// - location: The line/column of the error (1-based)
77+
// - highlightLen: Number of characters to highlight; 1 renders a single caret
78+
//
79+
// Returns the formatted context block, or an empty string if location is invalid.
5180
func FormatMultiLineContext(sql string, location models.Location, highlightLen int) string {
5281
if sql == "" || location.Line <= 0 {
5382
return ""
@@ -107,8 +136,18 @@ func FormatMultiLineContext(sql string, location models.Location, highlightLen i
107136
return sb.String()
108137
}
109138

110-
// FormatErrorSummary provides a brief summary of an error without full context
111-
// Useful for logging or when SQL context is not needed
139+
// FormatErrorSummary provides a concise one-line error summary suitable for
140+
// structured logging and monitoring systems where a full context window would
141+
// be too verbose. For *Error values the output format is:
142+
//
143+
// [E2001] unexpected token: COMMA at line 5, column 20
144+
//
145+
// For other error types the output is "Error: <message>".
146+
//
147+
// Parameters:
148+
// - err: The error to summarise
149+
//
150+
// Returns the one-line summary string.
112151
func FormatErrorSummary(err error) string {
113152
if structErr, ok := err.(*Error); ok {
114153
return fmt.Sprintf("[%s] %s at line %d, column %d",
@@ -120,7 +159,20 @@ func FormatErrorSummary(err error) string {
120159
return fmt.Sprintf("Error: %v", err)
121160
}
122161

123-
// FormatErrorWithSuggestion formats an error with an intelligent suggestion
162+
// FormatErrorWithSuggestion creates and formats a structured error that includes a
163+
// manually provided hint. When suggestion is empty, the function falls back to
164+
// auto-generating a hint via GenerateHint. This is the preferred formatter when
165+
// the caller already knows the correct fix.
166+
//
167+
// Parameters:
168+
// - code: ErrorCode classifying the error category
169+
// - message: Human-readable description of the error
170+
// - location: Precise line/column where the error occurred
171+
// - sql: Full SQL source used to generate the context window
172+
// - highlightLen: Number of characters to highlight at the error column
173+
// - suggestion: Custom hint text; empty string triggers auto-generation
174+
//
175+
// Returns the complete formatted error string.
124176
func FormatErrorWithSuggestion(code ErrorCode, message string, location models.Location, sql string, highlightLen int, suggestion string) string {
125177
err := NewError(code, message, location)
126178
err = err.WithContext(sql, highlightLen)
@@ -137,7 +189,16 @@ func FormatErrorWithSuggestion(code ErrorCode, message string, location models.L
137189
return err.Error()
138190
}
139191

140-
// FormatErrorList formats multiple errors in a readable list
192+
// FormatErrorList formats a slice of structured errors into a numbered list with
193+
// full context for each entry. The output begins with a count line and separates
194+
// each error with a blank line.
195+
//
196+
// Returns "No errors" when the slice is empty.
197+
//
198+
// Parameters:
199+
// - errors: Slice of *Error values to format
200+
//
201+
// Returns the multi-error report string.
141202
func FormatErrorList(errors []*Error) string {
142203
if len(errors) == 0 {
143204
return "No errors"
@@ -155,7 +216,21 @@ func FormatErrorList(errors []*Error) string {
155216
return sb.String()
156217
}
157218

158-
// FormatErrorWithExample formats an error with a corrected example
219+
// FormatErrorWithExample formats an error and appends a side-by-side "Wrong / Correct"
220+
// example to the hint. This is particularly useful for educational error messages
221+
// (e.g., in linters or IDEs) where showing the correct pattern helps users learn
222+
// the expected SQL syntax.
223+
//
224+
// Parameters:
225+
// - code: ErrorCode classifying the error category
226+
// - message: Human-readable description of the error
227+
// - location: Precise line/column where the error occurred
228+
// - sql: Full SQL source used to generate the context window
229+
// - highlightLen: Number of characters to highlight at the error column
230+
// - wrongExample: The erroneous SQL fragment (e.g., "SELECT * FORM users")
231+
// - correctExample: The corrected SQL fragment (e.g., "SELECT * FROM users")
232+
//
233+
// Returns the complete formatted error string including the before/after example.
159234
func FormatErrorWithExample(code ErrorCode, message string, location models.Location, sql string, highlightLen int, wrongExample, correctExample string) string {
160235
err := NewError(code, message, location)
161236
err = err.WithContext(sql, highlightLen)
@@ -167,7 +242,19 @@ func FormatErrorWithExample(code ErrorCode, message string, location models.Loca
167242
return err.Error()
168243
}
169244

170-
// FormatContextWindow formats a larger context window (up to N lines before and after)
245+
// FormatContextWindow formats a configurable SQL context window of up to linesBefore
246+
// lines before and linesAfter lines after the error line. Prefer this over
247+
// FormatMultiLineContext when more surrounding context is needed (e.g., in IDE
248+
// hover messages or verbose diagnostic reports).
249+
//
250+
// Parameters:
251+
// - sql: The full SQL source string
252+
// - location: The line/column of the error (1-based)
253+
// - highlightLen: Number of characters to highlight at the error column
254+
// - linesBefore: Number of source lines to display before the error line
255+
// - linesAfter: Number of source lines to display after the error line
256+
//
257+
// Returns the formatted context block, or an empty string if location is invalid.
171258
func FormatContextWindow(sql string, location models.Location, highlightLen int, linesBefore, linesAfter int) string {
172259
if sql == "" || location.Line <= 0 {
173260
return ""
@@ -231,21 +318,40 @@ func FormatContextWindow(sql string, location models.Location, highlightLen int,
231318
return sb.String()
232319
}
233320

234-
// IsStructuredError checks if an error is a structured GoSQLX error
321+
// IsStructuredError reports whether err is a GoSQLX *Error value.
322+
// Use this to distinguish GoSQLX structured errors from generic Go errors
323+
// before calling functions that require *Error (e.g., ExtractLocation).
324+
//
325+
// Example:
326+
//
327+
// if errors.IsStructuredError(err) {
328+
// loc, _ := errors.ExtractLocation(err)
329+
// // use loc for IDE diagnostics
330+
// }
235331
func IsStructuredError(err error) bool {
236332
_, ok := err.(*Error)
237333
return ok
238334
}
239335

240-
// ExtractLocation extracts location information from an error
336+
// ExtractLocation extracts the line/column location from a GoSQLX *Error.
337+
// This is the preferred way to obtain location data for IDE integrations such as
338+
// LSP diagnostics, since it handles the type assertion safely.
339+
//
340+
// Returns the Location and true when err is a *Error; returns a zero Location
341+
// and false for all other error types.
241342
func ExtractLocation(err error) (models.Location, bool) {
242343
if structErr, ok := err.(*Error); ok {
243344
return structErr.Location, true
244345
}
245346
return models.Location{}, false
246347
}
247348

248-
// ExtractErrorCode extracts the error code from an error
349+
// ExtractErrorCode extracts the ErrorCode from a GoSQLX *Error.
350+
// Unlike GetCode, this function returns a boolean indicating whether the extraction
351+
// succeeded, making it suitable for use in type-switch–style handling.
352+
//
353+
// Returns the ErrorCode and true when err is a *Error; returns an empty string
354+
// and false for all other error types.
249355
func ExtractErrorCode(err error) (ErrorCode, bool) {
250356
if structErr, ok := err.(*Error); ok {
251357
return structErr.Code, true

0 commit comments

Comments
 (0)