From 33f7a78c11d4d5d25eaa4b9250180ae51211449c Mon Sep 17 00:00:00 2001 From: Stephen Howell Date: Wed, 18 Jan 2023 16:53:41 -0800 Subject: [PATCH] Implement ALTER TYPE SET SCHEMA --- .../postgresql/pgx/v4/go/db.go | 32 ++++++ .../postgresql/pgx/v4/go/models.go | 103 ++++++++++++++++++ .../postgresql/pgx/v4/go/query.sql.go | 34 ++++++ .../postgresql/pgx/v4/query.sql | 17 +++ .../postgresql/pgx/v4/sqlc.json | 13 +++ .../postgresql/pgx/v5/go/db.go | 32 ++++++ .../postgresql/pgx/v5/go/models.go | 103 ++++++++++++++++++ .../postgresql/pgx/v5/go/query.sql.go | 34 ++++++ .../postgresql/pgx/v5/query.sql | 17 +++ .../postgresql/pgx/v5/sqlc.json | 13 +++ .../postgresql/stdlib/go/db.go | 31 ++++++ .../postgresql/stdlib/go/models.go | 103 ++++++++++++++++++ .../postgresql/stdlib/go/query.sql.go | 37 +++++++ .../postgresql/stdlib/query.sql | 17 +++ .../postgresql/stdlib/sqlc.json | 12 ++ internal/engine/postgresql/parse.go | 10 ++ .../sql/ast/alter_type_set_schema_stmt.go | 10 ++ internal/sql/astutils/walk.go | 5 + internal/sql/catalog/catalog.go | 3 + internal/sql/catalog/types.go | 48 ++++++++ 20 files changed, 674 insertions(+) create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/db.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/models.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/query.sql.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/query.sql create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/sqlc.json create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/db.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/models.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/query.sql.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/query.sql create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/sqlc.json create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/query.sql.go create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/query.sql create mode 100644 internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/sqlc.json create mode 100644 internal/sql/ast/alter_type_set_schema_stmt.go diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/db.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/db.go new file mode 100644 index 0000000000..7fb8bea331 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/models.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/models.go new file mode 100644 index 0000000000..2742f9817d --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/models.go @@ -0,0 +1,103 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "database/sql/driver" + "fmt" +) + +type Level string + +const ( + LevelDEBUG Level = "DEBUG" + LevelINFO Level = "INFO" + LevelWARN Level = "WARN" + LevelERROR Level = "ERROR" + LevelFATAL Level = "FATAL" +) + +func (e *Level) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = Level(s) + case string: + *e = Level(s) + default: + return fmt.Errorf("unsupported scan type for Level: %T", src) + } + return nil +} + +type NullLevel struct { + Level Level + Valid bool // Valid is true if Level is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullLevel) Scan(value interface{}) error { + if value == nil { + ns.Level, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.Level.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullLevel) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.Level), nil +} + +type NewEvent string + +const ( + NewEventSTART NewEvent = "START" + NewEventSTOP NewEvent = "STOP" +) + +func (e *NewEvent) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = NewEvent(s) + case string: + *e = NewEvent(s) + default: + return fmt.Errorf("unsupported scan type for NewEvent: %T", src) + } + return nil +} + +type NullNewEvent struct { + NewEvent NewEvent + Valid bool // Valid is true if NewEvent is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullNewEvent) Scan(value interface{}) error { + if value == nil { + ns.NewEvent, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.NewEvent.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullNewEvent) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.NewEvent), nil +} + +type LogLine struct { + ID int64 + Status NewEvent + Level Level +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/query.sql.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/query.sql.go new file mode 100644 index 0000000000..fc97d55b11 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/go/query.sql.go @@ -0,0 +1,34 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const listAuthors = `-- name: ListAuthors :many +SELECT id, status, level FROM log_lines +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]LogLine, error) { + rows, err := q.db.Query(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LogLine + for rows.Next() { + var i LogLine + if err := rows.Scan(&i.ID, &i.Status, &i.Level); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/query.sql b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/query.sql new file mode 100644 index 0000000000..06d156849a --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/query.sql @@ -0,0 +1,17 @@ +CREATE SCHEMA old; +CREATE SCHEMA new; + +CREATE TYPE event AS enum ('START', 'STOP'); +CREATE TYPE old.level AS enum ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'); + +CREATE TABLE log_lines ( + id BIGSERIAL PRIMARY KEY, + status event NOT NULL, + level old.level NOT NULL +); + +ALTER TYPE event SET SCHEMA new; +ALTER TYPE old.level SET SCHEMA public; + +-- name: ListAuthors :many +SELECT * FROM log_lines; diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/sqlc.json b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/sqlc.json new file mode 100644 index 0000000000..9403bd0279 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v4/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/db.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/db.go new file mode 100644 index 0000000000..a213f04680 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/models.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/models.go new file mode 100644 index 0000000000..2742f9817d --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/models.go @@ -0,0 +1,103 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "database/sql/driver" + "fmt" +) + +type Level string + +const ( + LevelDEBUG Level = "DEBUG" + LevelINFO Level = "INFO" + LevelWARN Level = "WARN" + LevelERROR Level = "ERROR" + LevelFATAL Level = "FATAL" +) + +func (e *Level) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = Level(s) + case string: + *e = Level(s) + default: + return fmt.Errorf("unsupported scan type for Level: %T", src) + } + return nil +} + +type NullLevel struct { + Level Level + Valid bool // Valid is true if Level is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullLevel) Scan(value interface{}) error { + if value == nil { + ns.Level, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.Level.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullLevel) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.Level), nil +} + +type NewEvent string + +const ( + NewEventSTART NewEvent = "START" + NewEventSTOP NewEvent = "STOP" +) + +func (e *NewEvent) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = NewEvent(s) + case string: + *e = NewEvent(s) + default: + return fmt.Errorf("unsupported scan type for NewEvent: %T", src) + } + return nil +} + +type NullNewEvent struct { + NewEvent NewEvent + Valid bool // Valid is true if NewEvent is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullNewEvent) Scan(value interface{}) error { + if value == nil { + ns.NewEvent, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.NewEvent.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullNewEvent) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.NewEvent), nil +} + +type LogLine struct { + ID int64 + Status NewEvent + Level Level +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/query.sql.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/query.sql.go new file mode 100644 index 0000000000..fc97d55b11 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/go/query.sql.go @@ -0,0 +1,34 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const listAuthors = `-- name: ListAuthors :many +SELECT id, status, level FROM log_lines +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]LogLine, error) { + rows, err := q.db.Query(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LogLine + for rows.Next() { + var i LogLine + if err := rows.Scan(&i.ID, &i.Status, &i.Level); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/query.sql b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/query.sql new file mode 100644 index 0000000000..06d156849a --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/query.sql @@ -0,0 +1,17 @@ +CREATE SCHEMA old; +CREATE SCHEMA new; + +CREATE TYPE event AS enum ('START', 'STOP'); +CREATE TYPE old.level AS enum ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'); + +CREATE TABLE log_lines ( + id BIGSERIAL PRIMARY KEY, + status event NOT NULL, + level old.level NOT NULL +); + +ALTER TYPE event SET SCHEMA new; +ALTER TYPE old.level SET SCHEMA public; + +-- name: ListAuthors :many +SELECT * FROM log_lines; diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/sqlc.json new file mode 100644 index 0000000000..6645ccbd1b --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/pgx/v5/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v5", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/db.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/db.go new file mode 100644 index 0000000000..eea4166ec6 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/models.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/models.go new file mode 100644 index 0000000000..2742f9817d --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/models.go @@ -0,0 +1,103 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 + +package querytest + +import ( + "database/sql/driver" + "fmt" +) + +type Level string + +const ( + LevelDEBUG Level = "DEBUG" + LevelINFO Level = "INFO" + LevelWARN Level = "WARN" + LevelERROR Level = "ERROR" + LevelFATAL Level = "FATAL" +) + +func (e *Level) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = Level(s) + case string: + *e = Level(s) + default: + return fmt.Errorf("unsupported scan type for Level: %T", src) + } + return nil +} + +type NullLevel struct { + Level Level + Valid bool // Valid is true if Level is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullLevel) Scan(value interface{}) error { + if value == nil { + ns.Level, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.Level.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullLevel) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.Level), nil +} + +type NewEvent string + +const ( + NewEventSTART NewEvent = "START" + NewEventSTOP NewEvent = "STOP" +) + +func (e *NewEvent) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = NewEvent(s) + case string: + *e = NewEvent(s) + default: + return fmt.Errorf("unsupported scan type for NewEvent: %T", src) + } + return nil +} + +type NullNewEvent struct { + NewEvent NewEvent + Valid bool // Valid is true if NewEvent is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullNewEvent) Scan(value interface{}) error { + if value == nil { + ns.NewEvent, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.NewEvent.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullNewEvent) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.NewEvent), nil +} + +type LogLine struct { + ID int64 + Status NewEvent + Level Level +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/query.sql.go b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/query.sql.go new file mode 100644 index 0000000000..b000aac1cf --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.16.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const listAuthors = `-- name: ListAuthors :many +SELECT id, status, level FROM log_lines +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]LogLine, error) { + rows, err := q.db.QueryContext(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LogLine + for rows.Next() { + var i LogLine + if err := rows.Scan(&i.ID, &i.Status, &i.Level); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/query.sql b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/query.sql new file mode 100644 index 0000000000..06d156849a --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/query.sql @@ -0,0 +1,17 @@ +CREATE SCHEMA old; +CREATE SCHEMA new; + +CREATE TYPE event AS enum ('START', 'STOP'); +CREATE TYPE old.level AS enum ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'); + +CREATE TABLE log_lines ( + id BIGSERIAL PRIMARY KEY, + status event NOT NULL, + level old.level NOT NULL +); + +ALTER TYPE event SET SCHEMA new; +ALTER TYPE old.level SET SCHEMA public; + +-- name: ListAuthors :many +SELECT * FROM log_lines; diff --git a/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/sqlc.json b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/sqlc.json new file mode 100644 index 0000000000..c72b6132d5 --- /dev/null +++ b/internal/endtoend/testdata/ddl_alter_type_set_schema/postgresql/stdlib/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "query.sql", + "queries": "query.sql" + } + ] +} diff --git a/internal/engine/postgresql/parse.go b/internal/engine/postgresql/parse.go index cf30bd4b6a..c05b5e00b9 100644 --- a/internal/engine/postgresql/parse.go +++ b/internal/engine/postgresql/parse.go @@ -225,6 +225,16 @@ func translate(node *nodes.Node) (ast.Node, error) { Table: rel.TableName(), NewSchema: makeString(n.Newschema), }, nil + + case nodes.ObjectType_OBJECT_TYPE: + rel, err := parseRelation(n.Object) + if err != nil { + return nil, err + } + return &ast.AlterTypeSetSchemaStmt{ + Type: rel.TypeName(), + NewSchema: makeString(n.Newschema), + }, nil } return nil, errSkip diff --git a/internal/sql/ast/alter_type_set_schema_stmt.go b/internal/sql/ast/alter_type_set_schema_stmt.go new file mode 100644 index 0000000000..22206d85cd --- /dev/null +++ b/internal/sql/ast/alter_type_set_schema_stmt.go @@ -0,0 +1,10 @@ +package ast + +type AlterTypeSetSchemaStmt struct { + Type *TypeName + NewSchema *string +} + +func (n *AlterTypeSetSchemaStmt) Pos() int { + return 0 +} diff --git a/internal/sql/astutils/walk.go b/internal/sql/astutils/walk.go index d1b4ee6aa8..d9591c04ec 100644 --- a/internal/sql/astutils/walk.go +++ b/internal/sql/astutils/walk.go @@ -44,6 +44,11 @@ func Walk(f Visitor, node ast.Node) { Walk(f, n.Type) } + case *ast.AlterTypeSetSchemaStmt: + if n.Type != nil { + Walk(f, n.Type) + } + case *ast.AlterTypeRenameValueStmt: if n.Type != nil { Walk(f, n.Type) diff --git a/internal/sql/catalog/catalog.go b/internal/sql/catalog/catalog.go index 798a636d8c..5b2b6b8a8d 100644 --- a/internal/sql/catalog/catalog.go +++ b/internal/sql/catalog/catalog.go @@ -61,6 +61,9 @@ func (c *Catalog) Update(stmt ast.Statement, colGen columnGenerator) error { case *ast.AlterTypeRenameValueStmt: err = c.alterTypeRenameValue(n) + case *ast.AlterTypeSetSchemaStmt: + err = c.alterTypeSetSchema(n) + case *ast.CommentOnColumnStmt: err = c.commentOnColumn(n) diff --git a/internal/sql/catalog/types.go b/internal/sql/catalog/types.go index 74f6389f58..8b9c656411 100644 --- a/internal/sql/catalog/types.go +++ b/internal/sql/catalog/types.go @@ -214,6 +214,54 @@ func (c *Catalog) alterTypeAddValue(stmt *ast.AlterTypeAddValueStmt) error { return nil } +func (c *Catalog) alterTypeSetSchema(stmt *ast.AlterTypeSetSchemaStmt) error { + ns := stmt.Type.Schema + if ns == "" { + ns = c.DefaultSchema + } + oldSchema, err := c.getSchema(ns) + if err != nil { + return err + } + typ, idx, err := oldSchema.getType(stmt.Type) + if err != nil { + return err + } + oldType := *stmt.Type + stmt.Type.Schema = *stmt.NewSchema + newSchema, err := c.getSchema(*stmt.NewSchema) + if err != nil { + return err + } + // Because tables have associated data types, the type name must also + // be distinct from the name of any existing table in the same + // schema. + // https://www.postgresql.org/docs/current/sql-createtype.html + tbl := &ast.TableName{ + Name: stmt.Type.Name, + } + if _, _, err := newSchema.getTable(tbl); err == nil { + return sqlerr.RelationExists(tbl.Name) + } + if _, _, err := newSchema.getType(stmt.Type); err == nil { + return sqlerr.TypeExists(stmt.Type.Name) + } + oldSchema.Types = append(oldSchema.Types[:idx], oldSchema.Types[idx+1:]...) + newSchema.Types = append(newSchema.Types, typ) + + // Update all the table columns with the new type + for _, schema := range c.Schemas { + for _, table := range schema.Tables { + for _, column := range table.Columns { + if column.Type == oldType { + column.Type.Schema = *stmt.NewSchema + } + } + } + } + return nil +} + func (c *Catalog) dropType(stmt *ast.DropTypeStmt) error { for _, name := range stmt.Types { ns := name.Schema