Skip to content

[feat] add SkipClassificationCheck#343

Merged
muir merged 2 commits intomainfrom
classifyCrateProcedure
Apr 3, 2026
Merged

[feat] add SkipClassificationCheck#343
muir merged 2 commits intomainfrom
classifyCrateProcedure

Conversation

@muir
Copy link
Copy Markdown
Owner

@muir muir commented Apr 2, 2026

Add migration option to skip classification check and allow mixing DML & DDL in the same migration.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.72%. Comparing base (fc809ce) to head (037b08f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #343      +/-   ##
==========================================
+ Coverage   84.60%   84.72%   +0.12%     
==========================================
  Files          16       16              
  Lines        1585     1591       +6     
==========================================
+ Hits         1341     1348       +7     
+ Misses        141      140       -1     
  Partials      103      103              
Flag Coverage Δ
go_tests 20.86% <0.00%> (+0.11%) ⬆️
mysql_tests 49.90% <85.71%> (+0.19%) ⬆️
pg_tests 54.11% <50.00%> (+0.12%) ⬆️
singlestore_tests 46.54% <57.14%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in migration option to bypass SQL classification-based validations so that stored-procedure style migrations (where DDL text contains DML tokens) can run without being rejected by the DDL/DML checks.

Changes:

  • Add SkipClassificationCheck() migration option and plumb it through MigrationBase.
  • Update MySQL and Postgres drivers to conditionally bypass specific classification validations when the option is set.
  • Add tests covering the MySQL behavior and add classifier-focused procedure test cases.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
api.go Adds the new migration option flag + getter to MigrationBase.
lsmysql/mysql.go Skips DDL/DML and idempotency validations when SkipClassificationCheck() is set.
lspostgres/postgres.go Skips specific non-transactional classification validations when SkipClassificationCheck() is set.
lsmysql/skip_test.go Adds an integration test that a mixed DDL+DML migration succeeds when the option is set.
classifysql/classify_procedure_test.go Adds test cases documenting current stored procedure tokenization/classification behavior.

api.go Outdated
Name MigrationName
async bool
rawAfter []MigrationName
order int // overall desired ordring across all libraries, ignores runAfter
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The inline comment on the order field contains a typo ("ordring"). Please correct it to "ordering" to keep struct field docs clear/searchable.

Suggested change
order int // overall desired ordring across all libraries, ignores runAfter
order int // overall desired ordering across all libraries, ignores runAfter

Copilot uses AI. Check for mistakes.
api.go Outdated
Comment on lines +333 to +339
// SkipClassificationCheck overrides checks that prevent checking
func SkipClassificationCheck() MigrationOption {
return func(m Migration) {
b := m.Base()
b.skipClassification = true
}
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The doc comment for SkipClassificationCheck is incomplete/vague ("overrides checks that prevent checking"). Since this is an exported option with potentially risky behavior, please update the comment to explicitly state which validations are bypassed (e.g., DDL/DML mixing and idempotency heuristics) and the intended use case (stored procedure/function definitions).

Copilot uses AI. Check for mistakes.
api.go Outdated
Comment on lines +437 to +439
// SkipClassification reports if SkipClassificationCheck() was explicitly called.
func (m *MigrationBase) SkipClassification() bool { return m.skipClassification }

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

SkipClassificationCheck() sets skipClassification, but the getter is named SkipClassification(). This is inconsistent with other option/getter pairs in this file (e.g., PreserveComments/PreserveComments, RepeatUntilNoOp/RepeatUntilNoOp) and can be confusing for maintainers. Consider renaming the getter to SkipClassificationCheck() (or SkipClassificationCheckEnabled() / HasSkipClassificationCheck()) to match the option name/pattern.

Suggested change
// SkipClassification reports if SkipClassificationCheck() was explicitly called.
func (m *MigrationBase) SkipClassification() bool { return m.skipClassification }
// SkipClassificationCheck reports if SkipClassificationCheck() was explicitly called.
func (m *MigrationBase) SkipClassificationCheck() bool { return m.skipClassification }
// Deprecated: use SkipClassificationCheck.
func (m *MigrationBase) SkipClassification() bool { return m.SkipClassificationCheck() }

Copilot uses AI. Check for mistakes.
lsmysql/mysql.go Outdated
Comment on lines +270 to +276
if !m.Base().SkipClassification() {
if summary.Includes(classifysql.IsDML) {
return errors.Errorf("mixed DDL and DML: %w", libschema.ErrDataAndDDL)
}
if m.Base().ForcedTransactional() {
return errors.Errorf("cannot force transactional on MySQL migration %s containing DDL", m.Base().Name)
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

SkipClassificationCheck currently skips all validation inside the DDL branch, including the guard that rejects ForceTransactional() migrations containing DDL. This makes ForceTransactional()+SkipClassificationCheck() silently allow a combination that was previously rejected and is called out as a foot-gun in the ForceTransactional docs (DDL autocommits on MySQL/SingleStore). Please keep the ForcedTransactional() + DDL rejection in place even when classification checks are skipped, or gate it behind a separate, explicitly named escape hatch.

Suggested change
if !m.Base().SkipClassification() {
if summary.Includes(classifysql.IsDML) {
return errors.Errorf("mixed DDL and DML: %w", libschema.ErrDataAndDDL)
}
if m.Base().ForcedTransactional() {
return errors.Errorf("cannot force transactional on MySQL migration %s containing DDL", m.Base().Name)
}
if m.Base().ForcedTransactional() {
return errors.Errorf("cannot force transactional on MySQL migration %s containing DDL", m.Base().Name)
}
if !m.Base().SkipClassification() {
if summary.Includes(classifysql.IsDML) {
return errors.Errorf("mixed DDL and DML: %w", libschema.ErrDataAndDDL)
}

Copilot uses AI. Check for mistakes.
Comment on lines 262 to 266
"sql": firstReal.Tokens.String(),
})
}
if example, ok := statements.Summarize()[classifysql.IsDML]; ok {
if example, ok := statements.Summarize()[classifysql.IsDML]; ok && !m.Base().SkipClassification() {
return errors.Wrapf(libschema.ErrNonIdempotentNonTx, "non-transactional migration %s contains data manipulation language: %s", m.Base().Name, example.Strip().String())
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Postgres non-transactional validation now allows bypassing the DML check when SkipClassificationCheck() is set, but there is no test exercising this new behavior in the existing lspostgres/*_test.go suite. Please add a Postgres test that demonstrates the option is needed (fails without it, succeeds with it) for the intended use case.

Copilot generated this review using guidance from repository custom instructions.
@muir
Copy link
Copy Markdown
Owner Author

muir commented Apr 2, 2026

@copilot apply changes based on the comments in this thread. Note: do not mark something deprecated that was introduce with this PR. Remove it instead.

Co-authored-by: David Sharnoff <muir@users.noreply.github.com>
@cursor cursor bot temporarily deployed to singlestore April 3, 2026 00:13 Inactive
@muir muir merged commit b935f65 into main Apr 3, 2026
21 of 22 checks passed
@muir muir deleted the classifyCrateProcedure branch April 3, 2026 00:22
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

@copilot apply changes based on the comments in this thread. Note: do not mark something deprecated that was introduce with ...

All changes from the review thread have been applied in commit 037b08f:

  • Fixed typo "ordring" → "ordering" in order field comment
  • Updated SkipClassificationCheck doc comment to explicitly describe the bypassed validations and intended use case
  • Renamed getter SkipClassification()SkipClassificationCheck() (removed the old method entirely, no deprecated shim)
  • MySQL: ForcedTransactional() + DDL check now runs unconditionally before the SkipClassificationCheck() gate
  • Postgres: added tests demonstrating failure without SkipClassificationCheck() and success with it (TestPostgresNonTxCases + TestPostgresSkipClassificationCheckAllowsNonTxDML)

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.

4 participants