Skip to content

Commit 101d498

Browse files
authored
Add comment handling and related tests for migration functionality (#344)
1 parent 25cee51 commit 101d498

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

migrate/export_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ package migrate
33
func IsCallback(stmt string) (name string) {
44
return isCallback(stmt)
55
}
6+
7+
func IsComment(stmt string) bool {
8+
return isComment(stmt)
9+
}

migrate/migrate.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ func FromFS(ctx context.Context, session gocqlx.Session, f fs.FS) error {
201201
return nil
202202
}
203203

204+
// applyMigration executes a single migration file by parsing and applying its statements.
205+
// It handles three types of content in migration files:
206+
// - SQL statements: executed against the database
207+
// - Callback commands: processed via registered callback handlers (format: -- CALL function_name;)
208+
// - Regular comments: silently skipped (format: -- any comment text)
209+
//
210+
// The function maintains migration state by tracking the number of completed statements,
211+
// allowing for resumption of partially completed migrations.
212+
//
213+
// Parameters:
214+
// - ctx: context for cancellation and timeouts
215+
// - session: database session for executing statements
216+
// - f: filesystem containing the migration file
217+
// - path: path to the migration file within the filesystem
218+
// - done: number of statements already completed (for resuming partial migrations)
219+
//
220+
// Returns an error if the migration fails at any point.
204221
func applyMigration(ctx context.Context, session gocqlx.Session, f fs.FS, path string, done int) error {
205222
file, err := f.Open(path)
206223
if err != nil {
@@ -272,19 +289,23 @@ func applyMigration(ctx context.Context, session gocqlx.Session, f fs.FS, path s
272289
// trim new lines and all whitespace characters
273290
stmt = strings.TrimSpace(stmt)
274291

292+
// Process statement based on its type
275293
if cb := isCallback(stmt); cb != "" {
294+
// Handle callback commands (e.g., "-- CALL function_name;")
276295
if Callback == nil {
277296
return fmt.Errorf("statement %d: missing callback handler while trying to call %s", i, cb)
278297
}
279298
if err := Callback(ctx, session, CallComment, cb); err != nil {
280299
return fmt.Errorf("callback %s: %s", cb, err)
281300
}
282-
} else {
301+
} else if stmt != "" && !isComment(stmt) {
302+
// Execute SQL statements (skip empty statements and comments)
283303
q := session.ContextQuery(ctx, stmt, nil).RetryPolicy(nil)
284304
if err := q.ExecRelease(); err != nil {
285305
return fmt.Errorf("statement %d: %s", i, err)
286306
}
287307
}
308+
// Regular comments and empty statements are silently skipped
288309

289310
// update info
290311
info.Done = i
@@ -315,3 +336,10 @@ func isCallback(stmt string) (name string) {
315336
}
316337
return s[1]
317338
}
339+
340+
// isComment returns true if the statement is a SQL comment that should be ignored.
341+
// It distinguishes between regular comments (which should be skipped) and
342+
// callback commands (which should be processed).
343+
func isComment(stmt string) bool {
344+
return strings.HasPrefix(stmt, "--") && isCallback(stmt) == ""
345+
}

migrate/migrate_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,29 @@ func TestMigrationNoSemicolon(t *testing.T) {
160160
}
161161
}
162162

163+
func TestMigrationWithTrailingComment(t *testing.T) {
164+
session := gocqlxtest.CreateSession(t)
165+
defer session.Close()
166+
recreateTables(t, session)
167+
168+
if err := session.ExecStmt(migrateSchema); err != nil {
169+
t.Fatal(err)
170+
}
171+
172+
f := makeTestFS(0)
173+
// Create a migration with a trailing comment (this should reproduce the issue)
174+
migrationContent := fmt.Sprintf(insertMigrate, 0) + "; -- ttl 1 hour"
175+
f.WriteFile("0.cql", []byte(migrationContent), fs.ModePerm)
176+
177+
ctx := context.Background()
178+
if err := migrate.FromFS(ctx, session, f); err != nil {
179+
t.Fatal("Migration should succeed with trailing comment, but got error:", err)
180+
}
181+
if c := countMigrations(t, session); c != 1 {
182+
t.Fatal("expected 1 migration got", c)
183+
}
184+
}
185+
163186
func TestIsCallback(t *testing.T) {
164187
table := []struct {
165188
Name string
@@ -211,6 +234,60 @@ func TestIsCallback(t *testing.T) {
211234
}
212235
}
213236

237+
func TestIsComment(t *testing.T) {
238+
table := []struct {
239+
Name string
240+
Stmt string
241+
IsComment bool
242+
}{
243+
{
244+
Name: "CQL statement",
245+
Stmt: "SELECT * from X;",
246+
IsComment: false,
247+
},
248+
{
249+
Name: "Regular comment",
250+
Stmt: "-- This is a comment",
251+
IsComment: true,
252+
},
253+
{
254+
Name: "Comment with additional text",
255+
Stmt: "-- ttl 1 hour",
256+
IsComment: true,
257+
},
258+
{
259+
Name: "Callback command (not a regular comment)",
260+
Stmt: "-- CALL Foo;",
261+
IsComment: false,
262+
},
263+
{
264+
Name: "Callback with spaces (not a regular comment)",
265+
Stmt: "-- CALL Bar;",
266+
IsComment: false,
267+
},
268+
{
269+
Name: "Empty statement",
270+
Stmt: "",
271+
IsComment: false,
272+
},
273+
{
274+
Name: "Whitespace only",
275+
Stmt: " ",
276+
IsComment: false,
277+
},
278+
}
279+
280+
for i := range table {
281+
test := table[i]
282+
t.Run(test.Name, func(t *testing.T) {
283+
result := migrate.IsComment(test.Stmt)
284+
if result != test.IsComment {
285+
t.Errorf("IsComment(%q) = %v, expected %v", test.Stmt, result, test.IsComment)
286+
}
287+
})
288+
}
289+
}
290+
214291
func TestMigrationCallback(t *testing.T) {
215292
var (
216293
beforeCalled int

0 commit comments

Comments
 (0)