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

Handle unconvertible CREATE TABLE statements #546

Merged
merged 9 commits into from
Dec 18, 2024
39 changes: 39 additions & 0 deletions pkg/sql2pgroll/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (

// convertCreateStmt converts a CREATE TABLE statement to a pgroll operation.
func convertCreateStmt(stmt *pgq.CreateStmt) (migrations.Operations, error) {
// Check if the statement can be converted
if !canConvertCreateStatement(stmt) {
return nil, nil
}

// Convert the column definitions
columns := make([]migrations.Column, 0, len(stmt.TableElts))
for _, elt := range stmt.TableElts {
column, err := convertColumnDef(elt.GetColumnDef())
Expand All @@ -29,6 +35,39 @@ func convertCreateStmt(stmt *pgq.CreateStmt) (migrations.Operations, error) {
}, nil
}

// canConvertCreateTableStatement returns true iff `stmt` can be converted to a
// pgroll operation.
func canConvertCreateStatement(stmt *pgq.CreateStmt) bool {
switch {
// Temporary and unlogged tables are not supported
case stmt.GetRelation().GetRelpersistence() != "p":
return false
// CREATE TABLE IF NOT EXISTS is not supported
case stmt.GetIfNotExists():
return false
// Table inheritance is not supported
case len(stmt.GetInhRelations()) != 0:
return false
// Paritioned tables are not supported
case stmt.GetPartspec() != nil:
return false
// Specifying an access method is not supported
case stmt.GetAccessMethod() != "":
return false
// Specifying storage options is not supported
case len(stmt.GetOptions()) != 0:
return false
// ON COMMIT options are not supported
case stmt.GetOncommit() != pgq.OnCommitAction_ONCOMMIT_NOOP:
return false
// Setting a tablespace is not supported
case stmt.GetTablespacename() != "":
return false
default:
return true
}
}

func convertColumnDef(col *pgq.ColumnDef) (*migrations.Column, error) {
// Convert the column type
typeString, err := pgq.DeparseTypeName(col.TypeName)
Expand Down
45 changes: 45 additions & 0 deletions pkg/sql2pgroll/create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,48 @@ func TestConvertCreateTableStatements(t *testing.T) {
})
}
}

func TestUnconvertableCreateTableStatements(t *testing.T) {
t.Parallel()

tests := []string{
// Temporary and unlogged tables are not supported
"CREATE TEMPORARY TABLE foo(a int)",
"CREATE UNLOGGED TABLE foo(a int)",

// The IF NOT EXISTS clause is not supported
"CREATE TABLE IF NOT EXISTS foo(a int)",

// Table inheritance is not supported
"CREATE TABLE foo(a int) INHERITS (bar)",

// Any kind of partitioning is not supported
"CREATE TABLE foo(a int) PARTITION BY RANGE (a)",
"CREATE TABLE foo(a int) PARTITION BY LIST (a)",

// Specifying a table access method is not supported
"CREATE TABLE foo(a int) USING bar",

// Specifying storage options is not supported
"CREATE TABLE foo(a int) WITH (fillfactor=70)",

// ON COMMMIT options are not supported. These options are syntactically
// valid for all tables, but Postgres will reject them for non-temporary
// tables. We err on the side of caution and reject them for all tables.
"CREATE TABLE foo(a int) ON COMMIT DROP",

// Specifying a tablespace is not supported
"CREATE TABLE foo(a int) TABLESPACE bar",
}

for _, sql := range tests {
t.Run(sql, func(t *testing.T) {
ops, err := sql2pgroll.Convert(sql)
require.NoError(t, err)

require.Len(t, ops, 1)

assert.Equal(t, expect.RawSQLOp(sql), ops[0])
})
}
}