Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate: supports INSERT Statement #109

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/spanner/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@ func (c *Client) ExecuteMigrations(ctx context.Context, migrations Migrations, l
}
}
case statementKindDML:
if _, err := c.ApplyDML(ctx, m.Statements, PriorityTypeUnspecified); err != nil {
return &Error{
Code: ErrorCodeExecuteMigrations,
err: err,
}
}
case statementKindPartitionedDML:
if _, err := c.ApplyPartitionedDML(ctx, m.Statements, PriorityTypeUnspecified); err != nil {
return &Error{
Code: ErrorCodeExecuteMigrations,
Expand Down
6 changes: 3 additions & 3 deletions pkg/spanner/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ func TestExecuteMigrations(t *testing.T) {
t.Fatalf("failed to execute migration: %v", err)
}

// ensure that 000003.sql and 000004.sql have been applied.
// ensure that 000003.sql, 000004.sql and 000005.sql have been applied.
ensureMigrationColumn(t, ctx, client, "LastName", "STRING(MAX)", "NO")
ensureMigrationVersionRecord(t, ctx, client, 4, false)
ensureMigrationVersionRecord(t, ctx, client, 5, false)

// ensure that schema is not changed and ExecuteMigrate is safely finished even though no migrations should be applied.
ensureMigrationColumn(t, ctx, client, "LastName", "STRING(MAX)", "NO")
ensureMigrationVersionRecord(t, ctx, client, 4, false)
ensureMigrationVersionRecord(t, ctx, client, 5, false)
}

func ensureMigrationColumn(t *testing.T, ctx context.Context, client *Client, columnName, spannerType, isNullable string) {
Expand Down
44 changes: 27 additions & 17 deletions pkg/spanner/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ var (

MigrationNameRegex = regexp.MustCompile(`[a-zA-Z0-9_\-]+`)

dmlRegex = regexp.MustCompile("^(INSERT|UPDATE|DELETE)[\t\n\f\r ].*")
dmlRegex = regexp.MustCompile("^(INSERT)[\t\n\f\r ].*")
partitionedDmlRegex = regexp.MustCompile("^(UPDATE|DELETE)[\t\n\f\r ].*")
)

const (
statementKindDDL statementKind = "DDL"
statementKindDML statementKind = "DML"
statementKindDDL statementKind = "DDL"
statementKindDML statementKind = "DML"
statementKindPartitionedDML statementKind = "PartitionedDML"
)

type (
Expand Down Expand Up @@ -170,30 +172,38 @@ func dmlToStatements(filename string, data []byte) ([]string, error) {
}

func inspectStatementsKind(statements []string) (statementKind, error) {
knwoop marked this conversation as resolved.
Show resolved Hide resolved
kindMap := map[statementKind]uint64{
statementKindDDL: 0,
statementKindDML: 0,
if len(statements) == 0 { // Treat empty files as DDL.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note: this is treated as a DDL under the existing specifications.

return statementKindDDL, nil
}

var hasDDL, hasDML, hasPartitionedDML bool
for _, s := range statements {
if isDML(s) {
kindMap[statementKindDML]++
} else {
kindMap[statementKindDDL]++
switch {
case isDML(s):
hasDML = true
case isPartitionedDML(s):
hasPartitionedDML = true
default:
hasDDL = true
}
}

if kindMap[statementKindDML] > 0 {
if kindMap[statementKindDDL] > 0 {
return "", errors.New("cannot specify DDL and DML at same migration file")
}

switch {
case hasDDL && !hasDML && !hasPartitionedDML:
return statementKindDDL, nil
case !hasDDL && hasDML && !hasPartitionedDML:
return statementKindDML, nil
case !hasDDL && !hasDML && hasPartitionedDML:
return statementKindPartitionedDML, nil
default:
return "", errors.New("DDL, DML (INSERT), and partitioned DML (UPDATE or DELETE) must not be combined in the same migration file")
}

return statementKindDDL, nil
}

func isDML(statement string) bool {
return dmlRegex.Match([]byte(statement))
}

func isPartitionedDML(statement string) bool {
return partitionedDmlRegex.Match([]byte(statement))
}
2 changes: 1 addition & 1 deletion pkg/spanner/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestLoadMigrations(t *testing.T) {
t.Fatal(err)
}

if len(ms) != 3 {
if len(ms) != 4 {
t.Fatalf("migrations length want 3, but got %v", len(ms))
}

Expand Down
1 change: 1 addition & 0 deletions pkg/spanner/testdata/migrations/000005.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO Singers (SingerID, FirstName, LastName) VALUES ("2", "Hoge", "Fuga");
Loading