Repository navigation
feat(cbinding,python): dialect-aware exports, lint, security scan, transpile (pygosqlx 0.3.0) - #850
Conversation
…anspile; pygosqlx 0.3.0
C ABI (pkg/cbinding): every existing export keeps its name, signature and
JSON output. New exports, all JSON-out and freed with gosqlx_free:
- gosqlx_{parse,validate,format,extract_tables,extract_columns,
extract_functions,extract_metadata}_dialect(sql, dialect): NULL/"" behaves
exactly like the original export; an unknown dialect returns the normal
error shape with error_code "E4002" and the list of valid names.
- gosqlx_lint(sql, options_json): the `gosqlx lint` CLI rule set
(L001-L010), options {"dialect", "max_line_length"}; unknown or invalid
options return error_code "INVALID_OPTIONS".
- gosqlx_security_scan(sql): pkg/sql/security ScanSQL with severity counts.
- gosqlx_transpile(sql, from, to): gosqlx.Transpile.
- gosqlx_dialects(): keywords.DialectNames().
The original exports now route through the same helpers with an empty
dialect, and a test pins their output byte-for-byte against the dialect
variants.
Python (pygosqlx 0.3.0): keyword-only dialect= on parse, validate,
validate_detailed, format and extract_*; new lint(), security_scan(),
transpile() and dialects() with LintResult/LintViolation and
SecurityScanResult/SecurityFinding dataclasses; UnsupportedDialectError
(GoSQLXError + ValueError) and TranspileError. An older shared library
keeps working without dialect= and raises GoSQLXError for the new calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Claude finished @ajitpratap0's task in 32s —— View job Review of PR #850 (pygosqlx 0.3.0 / cbinding dialect exports)I read Overall: The design is clean. The Issues
Security and performance
TestsCoverage is strong: parity, unknown dialects on every export, option-error cases, a concurrency test and the older-library paths in Python. The gap is the trailing-data case from item 1 and the dialect-versus-no-dialect format comparison from item 3. Nothing here blocks the merge. Item 1 is the only real bug, and it is small. I made no code changes. |
Standalone staticcheck (SA1019) ignores //nolint, so spell out the default line width instead of reading the deprecated field, and pin the generic dialect format output to gosqlx_format's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9
|
Claude finished @ajitpratap0's task in 15s —— View job Review of PR #850 (pygosqlx 0.3.0 / cbinding dialect exports)I checked Overall: The design is clean. The shared Issues
Security and performance
TestsCoverage is strong: parity, unknown dialects on every export, option error cases, concurrency, and the older-library paths. The gaps are the Nothing here blocks the merge, but I'd fix item 1 first. |
|
Claude finished @ajitpratap0's task in 16s —— View job Review of PR #850 (pygosqlx 0.3.0 / cbinding dialect exports)I re-checked the two code paths the earlier reviews flagged ( Overall: The design is sound. The shared Confirmed issues
Suggestions (non-blocking)
Security and performance
TestsCoverage is strong: parity, unknown dialects on every export, option-error cases, concurrency and the older-library paths. The gaps are the Nothing here blocks the merge, but I'd fix item 1 first. |
…anspile dialects
dec.More() returns false before a stray ']' or '}', so input like `{} }`
slipped past the trailing-data check. Require dec.Token() to hit io.EOF.
pygosqlx.transpile now rejects a missing from_dialect/to_dialect up front
with UnsupportedDialectError("<arg> is required").
Addresses claude-review findings on #850.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Claude finished @ajitpratap0's task in 13s —— View job Review of PR #850 (pygosqlx 0.3.0 / cbinding dialect exports)I checked the two items the earlier reviews flagged against the current branch head ( Both earlier blocking items are already fixed in
Remaining suggestions (non-blocking):
Security and performance: I saw no memory-safety concerns. Every new export returns a caller-owned string freed with Tests: Coverage is strong: parity, unknown dialects on every export, option-error cases, concurrency, and the older-library paths in Python. The one gap is the dialect-path format comparison above. Nothing blocks the merge. The remaining checks are the CI jobs listed as unchecked in the test plan. |
Summary
Brings the Python bindings up to date as pygosqlx 0.3.0, built on the gosqlx_version double-free fix (#847).
C ABI (
pkg/cbinding)All existing exports are ABI-stable. Their names, signatures and JSON output are unchanged. They now go through shared helpers with an empty dialect, and a new test checks that their output is byte-identical to the
*_dialectvariants when the dialect is NULL or"". New fields (error_code) areomitempty, so they never appear in the original exports' output.New exports. Every one returns a caller-owned JSON string that the caller frees with
gosqlx_free:gosqlx_{parse,validate,format,extract_tables,extract_columns,extract_functions,extract_metadata}_dialect(sql, dialect)""dialect gives the same result as the original export. An unknown dialect returns the operation's usual error shape plus"error_code": "E4002"(ErrCodeUnsupportedDialect) and a message listingkeywords.DialectNames().gosqlx_lint(sql, options_json)gosqlx lintCLI rule set (L001-L010, same configuration ascreateLinterincmd/gosqlx/cmd/lint.go, whichpkg/mcpalso mirrors). Options are{"dialect", "max_line_length"}(default 100, like--max-length). Unknown fields, malformed JSON, non-positive length or trailing data return"error_code": "INVALID_OPTIONS". SQL that does not parse is still linted by the text and token rules.gosqlx_security_scan(sql)security.Scanner.ScanSQL, the same raw-text scan the MCPsecurity_scantool uses. Returns findings, per-severity counts,is_clean,has_criticalandhas_high.gosqlx_transpile(sql, from, to)gosqlx.Transpile. Both dialects are required canonical names (missing or unknown ones giveE4002).gosqlx_dialects(){"dialects": keywords.DialectNames()}Python (
python/pygosqlx)parse,validate,validate_detailed,formatandextract_*take a keyword-onlydialect=argument. WithNone(the default) they call the original exports, so behaviour does not change.lint(),security_scan(),transpile()anddialects(). New dataclassesLintResult/LintViolationandSecurityScanResult/SecurityFinding. New exceptionsUnsupportedDialectError(subclasses bothGoSQLXErrorandValueError,code="E4002") andTranspileError.dialect=keep working.dialect=and the new functions raiseGoSQLXErrorwith a "rebuild the shared library" message. Symbols are bound through_bind_optionaland checked through_has_func.pyproject.toml,setup.pyandpygosqlx.__version__.python/README.mdAPI docs updated.Design decisions (scoped worker, conservative choices)
*_dialect(sql, dialect)variants instead of one options-JSON entry point per operation. A dialect is the only option these operations take, so a second C string keeps the ABI as simple as the original exports, and there is nothing to parse or validate.gosqlx_linttakesoptions_jsonbecause it really has more than one option. This is documented in the package doc comment.gosqlx.Lint(all 30 rules). The brief namedgosqlx lintas the source of truth, and the MCP server already mirrors it. The list is a third copy (CLI, MCP, cbinding), and a comment asks maintainers to keep them in sync. I did not extract a shared exported constructor because that would change public API outside this brief.ScanSQL(raw text) instead of the ASTScan. This matches the MCP tool, and it means injection payloads that do not parse still get scanned.postgres/mssqlaliases are not accepted, which keeps one vocabulary across everydialectargument.keywords.IsValidDialectand the CLI.CLAUDE.md's module dependency line forcbindingis updated to the new imports.build.shonly gets an updated hint line.Review follow-up (ee7646e)
gosqlx_lintoptions: the trailing-data check now requires EOF, so{} }and{} ]are rejected withINVALID_OPTIONS(dec.More()missed stray closers). Tests added.pygosqlx.transpile: a missing/emptyfrom_dialect/to_dialectnow raisesUnsupportedDialectError("<arg> is required")up front instead of round-tripping through the library. Conservative choice: keptUnsupportedDialectError(aValueError) rather than introducingTypeError, so existing behaviour/tests are unchanged.Test Plan
gofmt -l pkg/cbindingis clean.go vet ./pkg/cbinding/passes.golangci-lint run ./pkg/cbinding/...reports 0 issues.task test:cbinding(CGO,-race) passes. Newcbinding_ext_test.gocovers:""dialect, for each export and each of NULL, empty, whitespace, valid, multi-statement and invalid inputsTOP/brackets parsing only with the dialectfakesql,MySQL,postgresql) givingE4002for every exportmax_line_length, dialect, and 8 option error casesgosqlx_dialectsEvery result is freed via
gosqlx_freein the helpers.pytest python/testsagainst a freshly builtlibgosqlx.dylib: 184 passed. Without the library: 77 passed, the rest skipped. Newtest_dialect_and_analysis.pycovers every new function anddialect=path, older-library graceful errors (monkeypatched_has_func), argument validation, the result types and a threaded run.pkg/cbinding/tests/test_ctypes.py(raw ABI harness): 25 passed.Pythonworkflow from fix(cbinding): return caller-owned string from gosqlx_version; add Python CI #847 and the cbinding race jobBreaking Changes
None. Existing C exports and their JSON output are unchanged. Existing Python calls behave the same.
dialectis a new keyword-only argument with aNonedefault.Release note
pygosqlx 0.3.0 / C binding:
parse,validate,validate_detailed,formatandextract_*take an optionaldialect=argument (for exampledialect="sqlserver").pygosqlx.dialects()lists the supported names. An unknown dialect raisesUnsupportedDialectError, which is also aValueError.pygosqlx.lint()runs thegosqlx lintrule set (L001-L010, configurablemax_line_length).pygosqlx.security_scan()detects SQL injection patterns and returns typed findings with severity counts.pygosqlx.transpile(sql, from_dialect, to_dialect)converts SQL between dialects.gosqlx_*_dialect,gosqlx_lint,gosqlx_security_scan,gosqlx_transpileandgosqlx_dialects. Each returns a JSON string that the caller frees withgosqlx_free. The existing exports are unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9