Skip to content

Conversation

muir
Copy link
Owner

@muir muir commented Sep 27, 2025

Some PostgreSQL commands cannot be used inside transactions.

This change introduces the ability to do PostgreSQL migrations outside of a transaction. When defining a transaction with Generate() or Computed(), transactional or non-transactional will be determined by the type signature of the callback function.

For Script(), the text will be examined and a determination made based on command matching. To force it one way or the other, use the libschema.ForceTransactional() or libschema.ForceNonTransactional() migration options.

MySQL and SingleStore packages were updated to support the same pattern, though they already did most things non-transactionally because they don't support any DDL inside transactions.

Also changed in this PR:

  • Switched from google.com/pkg/errors to github.com/memsql/errors
  • Use sentinel errors instead for key situations

@codecov
Copy link

codecov bot commented Sep 27, 2025

Codecov Report

❌ Patch coverage is 86.22047% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.69%. Comparing base (5e8cd23) to head (f1bfd33).

Files with missing lines Patch % Lines
lspostgres/postgres.go 74.09% 30 Missing and 13 partials ⚠️
lsmysql/mysql.go 83.21% 16 Missing and 7 partials ⚠️
lssinglestore/singlestore.go 33.33% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #269      +/-   ##
==========================================
+ Coverage   79.79%   81.69%   +1.90%     
==========================================
  Files          12       15       +3     
  Lines        1282     1579     +297     
==========================================
+ Hits         1023     1290     +267     
- Misses        183      201      +18     
- Partials       76       88      +12     
Flag Coverage Δ
go_tests 20.39% <37.40%> (+11.42%) ⬆️
mysql_tests 48.62% <50.80%> (-0.68%) ⬇️
pg_tests 50.31% <46.10%> (-0.61%) ⬇️
singlestore_tests 45.17% <45.54%> (+0.79%) ⬆️

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.

@muir muir requested a review from Copilot October 7, 2025 19:27
Copy link

@Copilot 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 support for declaring and executing migrations outside of transactions (idempotent/non-transactional) across Postgres, MySQL, and SingleStore by introducing generic migration helpers (Script/Generate/Computed with *sql.Tx vs *sql.DB), statement classification (new internal/stmtclass), and a shared finalization orchestration (internal/migfinalize). Also replaces github.com/pkg/errors with github.com/memsql/errors and introduces sentinel errors for unsafe migration patterns.

  • Introduces generic APIs to infer transactional vs non-transactional behavior.
  • Adds SQL statement classifier and migration finalizer utilities.
  • Adds extensive new tests and documentation (NON_TRANSACTIONAL.md) plus sentinel errors & updated README.

Reviewed Changes

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

Show a summary per file
File Description
lstesting/testing_test.go Adds cleanup nil safety test.
lstesting/testing.go Adds defensive db.Ping and updated cleanup logic.
lssinglestore/* Adapts SingleStore driver & tests to generic migrations and new options.
lspostgres/postgres.go Major refactor: generic migrations, non-tx inference, statement classification, server version handling, finalizer integration.
lspostgres/* tests & docs New tests for inference, non-tx behavior, schema override, classification, failures; adds NON_TRANSACTIONAL.md.
lsmysql/mysql.go & related tests Mirrors Postgres generic migration pattern; classification & finalizer integration; mismatch tests.
lsmysql/check.go Re-implements script checks via stmtclass; re-exports sentinel errors.
internal/stmtclass/* New statement classification package and tests.
internal/migfinalize/* New reusable migration finalization utility and tests.
api.go / abstractions.go Adds sentinel errors, non-tx flags, ExecConn abstractions, RepeatUntilNoOp doc expansion, Force(Non)Transactional options.
apply.go / driver.go / dgorder/dependencies.go Updates to use new errors package; minor return cleanup.
README.md Adds non-transactional & RepeatUntilNoOp guidance (typo present).
logger.go Minor import ordering tweak.
go.mod Adds github.com/memsql/errors, sqltoken, updates indirect deps.
misc test files Parallelization and new coverage tests.
Comments suppressed due to low confidence (1)

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@muir muir requested a review from Copilot October 7, 2025 21:04
Copy link

@Copilot 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

Copilot reviewed 48 out of 49 changed files in this pull request and generated 8 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Collaborator

@dncohen dncohen left a comment

Choose a reason for hiding this comment

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

Nice. Looks like it does more than just out-of-transaction migrations. It looks like it increases test coverage a lot. All good, just wondering should it be split into smaller PRs?

abstractions.go Outdated
Comment on lines 49 to 50
// WARNING (foot-gun): On MySQL / SingleStore this DOES NOT make DDL atomic. Those engines
// autocommit each DDL statement regardless of any BEGIN you issue. By forcing transactional mode:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this make the migration fail on those engines? I'm asking is this comment enough?

@muir muir requested a review from Copilot October 15, 2025 23:08
Copy link

@Copilot 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

Copilot reviewed 56 out of 57 changed files in this pull request and generated 8 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@muir muir force-pushed the nonTransactional branch from dc79dd5 to 1d425d2 Compare October 16, 2025 19:10
@muir muir requested a review from Copilot October 16, 2025 19:10
Copy link

@Copilot 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

Copilot reviewed 55 out of 56 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@muir muir requested a review from Copilot October 16, 2025 19:52
Copy link

@Copilot 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

Copilot reviewed 55 out of 56 changed files in this pull request and generated no new comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Collaborator

@dncohen dncohen left a comment

Choose a reason for hiding this comment

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

still LGTM overall. Minor comments/questions in other threads.

Comment on lines +292 to +293
v := false // false means non-transactional
b.forcedTx = &v
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this use SetTransactional?


defer func() {
if tx != nil {
f.RollbackTx(tx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

looks weird to always rollback. I guess the idea is that it will do nothing after a commit, but still reads funny to me.

Comment on lines +45 to +47
// flagsInOrder provides a single stable ordering for iteration & presentation.
// (Replaces previous duplicated allFlags / flagNameOrder slices.)
var flagsInOrder = []Flag{IsMultipleStatements, IsDDL, IsDML, IsNonIdempotent, IsEasilyIdempotentFix, IsMustNonTx}
Copy link
Collaborator

Choose a reason for hiding this comment

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

why not just define them in order?

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.

2 participants