chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,494 @@
|
||||
// Copyright 2023 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeAlterTable handles *tree.AlterTable nodes.
|
||||
func nodeAlterTable(ctx *Context, node *tree.AlterTable) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
treeTableName := node.Table.ToTableName()
|
||||
tableName, err := nodeTableName(ctx, &treeTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't implement sequence addition/modification as a DDL since it's not in GMS, so we only support one of these
|
||||
// at a time. If there are more than one, then we'll return an error in the command loop.
|
||||
if len(node.Cmds) == 1 {
|
||||
cmd, ok := node.Cmds[0].(*tree.AlterTableComputed)
|
||||
if ok {
|
||||
return nodeAlterTableComputed(ctx, treeTableName, cmd)
|
||||
}
|
||||
if vcmd, ok := node.Cmds[0].(*tree.AlterTableValidateConstraint); ok {
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewValidateConstraint(
|
||||
tableName.SchemaQualifier.String(),
|
||||
tableName.Name.String(),
|
||||
bareIdentifier(vcmd.Constraint),
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
statements, noOps, err := nodeAlterTableCmds(ctx, node.Cmds, tableName, node.IfExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there are no valid statements return a no-op statement
|
||||
if len(noOps) > 0 && len(statements) == 0 {
|
||||
return NewNoOp(noOps...), nil
|
||||
}
|
||||
|
||||
// Otherwise emit warnings now, then return an AlterTable statement
|
||||
// TODO: we don't have a way to send or store the warnings alongside a valid AlterTable statement. We could either
|
||||
// get a *sql.Context here and emit warnings, or we could store the warnings in the Context and make the caller
|
||||
// emit them before it sends |ReadyForQuery|
|
||||
// if len(noOps) > 0 {
|
||||
// emit warnings
|
||||
// }
|
||||
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
Statements: statements,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetSchema handles *tree.AlterTableSetSchema nodes.
|
||||
func nodeAlterTableSetSchema(ctx *Context, node *tree.AlterTableSetSchema) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Errorf("ALTER TABLE SET SCHEMA is not yet supported")
|
||||
}
|
||||
|
||||
// nodeAlterTableCmds converts tree.AlterTableCmds into a slice of vitess.DDL instances that can be executed by GMS.
|
||||
// |tableName| identifies the table being altered, and |ifExists| indicates whether the IF EXISTS clause was specified.
|
||||
// A second slice of unsupported but safely ignored statements is return as well.
|
||||
func nodeAlterTableCmds(
|
||||
ctx *Context,
|
||||
node tree.AlterTableCmds,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) ([]*vitess.DDL, []string, error) {
|
||||
|
||||
if len(node) == 0 {
|
||||
return nil, nil, errors.Errorf("no commands specified for ALTER TABLE statement")
|
||||
}
|
||||
|
||||
vitessDdlCmds := make([]*vitess.DDL, 0, len(node))
|
||||
var unsupportedWarnings []string
|
||||
for _, cmd := range node {
|
||||
var err error
|
||||
var statement *vitess.DDL
|
||||
switch cmd := cmd.(type) {
|
||||
case *tree.AlterTableAddConstraint:
|
||||
statement, err = nodeAlterTableAddConstraint(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableAddColumn:
|
||||
statement, err = nodeAlterTableAddColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
|
||||
// If inline constraints have been specified, set the ConstraintAction so that they get processed
|
||||
if len(statement.TableSpec.Constraints) > 0 {
|
||||
statement.ConstraintAction = vitess.AddStr
|
||||
}
|
||||
|
||||
case *tree.AlterTableDropColumn:
|
||||
statement, err = nodeAlterTableDropColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableDropConstraint:
|
||||
statement, err = nodeAlterTableDropConstraint(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableRenameColumn:
|
||||
statement, err = nodeAlterTableRenameColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableSetDefault:
|
||||
statement, err = nodeAlterTableSetDefault(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableDropNotNull:
|
||||
statement, err = nodeAlterTableDropNotNull(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableSetNotNull:
|
||||
statement, err = nodeAlterTableSetNotNull(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableAlterColumnType:
|
||||
statement, err = nodeAlterTableAlterColumnType(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableOwner:
|
||||
unsupportedWarnings = append(unsupportedWarnings, fmt.Sprintf("ALTER TABLE %s OWNER TO %s", tableName.String(), cmd.Owner))
|
||||
case *tree.AlterTableComputed:
|
||||
return nil, nil, errors.New("This command does not currently support multiple actions in one statement")
|
||||
case *tree.AlterTableSetStatistics:
|
||||
// is unsupported and ignored
|
||||
case *tree.AlterTableRowLevelSecurity:
|
||||
// is unsupported and ignored
|
||||
case *tree.AlterTableReplicaIdentity:
|
||||
// is unsupported and ignored
|
||||
default:
|
||||
return nil, nil, errors.Errorf("ALTER TABLE with unsupported command type %T", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return vitessDdlCmds, unsupportedWarnings, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableAddConstraint converts a tree.AlterTableAddConstraint instance
|
||||
// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies
|
||||
// the table being altered, and |ifExists| indicates whether the IF EXISTS clause
|
||||
// was specified.
|
||||
func nodeAlterTableAddConstraint(
|
||||
ctx *Context,
|
||||
node *tree.AlterTableAddConstraint,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) (*vitess.DDL, error) {
|
||||
|
||||
notValid := node.ValidationBehavior == tree.ValidationSkip
|
||||
if notValid {
|
||||
if _, ok := node.ConstraintDef.(*tree.UniqueConstraintTableDef); ok {
|
||||
return nil, errors.Errorf("NOT VALID is not applicable to UNIQUE or PRIMARY KEY constraints")
|
||||
}
|
||||
}
|
||||
|
||||
switch constraintDef := node.ConstraintDef.(type) {
|
||||
case *tree.CheckConstraintTableDef:
|
||||
return nodeCheckConstraintTableDef(ctx, constraintDef, tableName, ifExists, notValid)
|
||||
case *tree.UniqueConstraintTableDef:
|
||||
return nodeUniqueConstraintTableDef(ctx, constraintDef, tableName, ifExists)
|
||||
case *tree.ForeignKeyConstraintTableDef:
|
||||
foreignKeyDefinition, err := nodeForeignKeyConstraintTableDef(ctx, constraintDef, notValid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ConstraintAction: "add",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
TableSpec: &vitess.TableSpec{
|
||||
Constraints: []*vitess.ConstraintDefinition{
|
||||
{
|
||||
Name: bareIdentifier(constraintDef.Name),
|
||||
Details: foreignKeyDefinition,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported constraint "+
|
||||
"definition type %T", node)
|
||||
}
|
||||
}
|
||||
|
||||
// bareIdentifier returns the string representation of a name without any quoting
|
||||
// (quoted is the default Name.String() behavior)
|
||||
func bareIdentifier(id tree.Name) string {
|
||||
ctx := tree.NewFmtCtx(tree.FmtBareIdentifiers)
|
||||
id.Format(ctx)
|
||||
return ctx.CloseAndGetString()
|
||||
}
|
||||
|
||||
// nodeAlterTableAddColumn converts a tree.AlterTableAddColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableAddColumn(ctx *Context, node *tree.AlterTableAddColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.IfNotExists {
|
||||
return nil, errors.Errorf("IF NOT EXISTS on a column in an ADD COLUMN statement is not supported yet")
|
||||
}
|
||||
|
||||
vitessColumnDef, err := nodeColumnTableDef(ctx, node.ColumnDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableSpec := &vitess.TableSpec{}
|
||||
tableSpec.AddColumn(vitessColumnDef)
|
||||
|
||||
if node.ColumnDef.References.Table != nil {
|
||||
constraintDef, err := nodeForeignKeyDefinitionFromColumnTableDef(ctx, node.ColumnDef.Name, node.ColumnDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tableSpec.AddConstraint(&vitess.ConstraintDefinition{
|
||||
Name: string(node.ColumnDef.References.ConstraintName),
|
||||
Details: constraintDef,
|
||||
})
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "add",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitessColumnDef.Name,
|
||||
TableSpec: tableSpec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableDropColumn converts a tree.AlterTableDropColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableDropColumn(ctx *Context, node *tree.AlterTableDropColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.IfExists {
|
||||
return nil, errors.Errorf("IF EXISTS on a column in a DROP COLUMN statement is not supported yet")
|
||||
}
|
||||
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("ALTER TABLE DROP COLUMN does not support RESTRICT option")
|
||||
case tree.DropCascade:
|
||||
logrus.Warnf("CASCADE option on DROP COLUMN is not yet supported, ignoring")
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported drop behavior %v", node.DropBehavior)
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "drop",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableRenameColumn converts a tree.AlterTableRenameColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableRenameColumn(ctx *Context, node *tree.AlterTableRenameColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "rename",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
ToColumn: vitess.NewColIdent(node.NewName.String()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetDefault converts a tree.AlterTableSetDefault instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableSetDefault(ctx *Context, node *tree.AlterTableSetDefault, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
expr, err := nodeExpr(ctx, node.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GMS requires the AST to wrap function expressions in parens
|
||||
if _, ok := expr.(*vitess.FuncExpr); ok {
|
||||
expr = &vitess.ParenExpr{Expr: expr}
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
DefaultSpec: &vitess.DefaultSpec{
|
||||
Action: "set",
|
||||
Column: vitess.NewColIdent(bareIdentifier(node.Column)),
|
||||
Value: expr,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableAlterColumnType converts a tree.AlterTableAlterColumnType instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableAlterColumnType(ctx *Context, node *tree.AlterTableAlterColumnType, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.Collation != "" {
|
||||
return nil, errors.Errorf("ALTER TABLE with COLLATE is not supported yet")
|
||||
}
|
||||
|
||||
if node.Using != nil {
|
||||
return nil, errors.Errorf("ALTER TABLE with USING is not supported yet")
|
||||
}
|
||||
|
||||
convertType, resolvedType, err := nodeResolvableTypeReference(ctx, node.ToType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resolvedType == pgtypes.Record {
|
||||
return nil, errors.Errorf(`column "%s" has pseudo-type record`, node.Column.String())
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
ColumnTypeSpec: &vitess.ColumnTypeSpec{
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
Type: vitess.ColumnType{
|
||||
Type: convertType.Type,
|
||||
ResolvedType: resolvedType,
|
||||
Length: convertType.Length,
|
||||
Scale: convertType.Scale,
|
||||
Charset: convertType.Charset,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableDropNotNull converts a tree.AlterTableDropNotNull instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableDropNotNull(ctx *Context, node *tree.AlterTableDropNotNull, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
NotNullSpec: &vitess.NotNullSpec{
|
||||
Action: "drop",
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetNotNull converts a tree.AlterTableSetNotNull instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableSetNotNull(ctx *Context, node *tree.AlterTableSetNotNull, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
NotNullSpec: &vitess.NotNullSpec{
|
||||
Action: "set",
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetNotNull converts a tree.AlterTablePartition instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTablePartition(ctx *Context, node *tree.AlterTablePartition) (*vitess.AlterTable, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: This is an incomplete translation because our GMS implementation doesn't support the MySQL
|
||||
// equivalent of these statements either. Regardless, these are all no-ops.
|
||||
treeTableName := node.Name.ToTableName()
|
||||
tableName, err := nodeTableName(ctx, &treeTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch node.Spec.Type {
|
||||
case tree.PartitionBoundIn:
|
||||
case tree.PartitionBoundFromTo:
|
||||
case tree.PartitionBoundWith:
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported partition type %v", node.Spec.Type)
|
||||
}
|
||||
|
||||
partitionDef := &vitess.PartitionDefinition{
|
||||
Name: vitess.NewColIdent(node.Partition.String()),
|
||||
}
|
||||
|
||||
actionStr := ""
|
||||
if node.IsDetach {
|
||||
actionStr = vitess.DropStr
|
||||
} else {
|
||||
actionStr = vitess.AddStr
|
||||
}
|
||||
|
||||
partitionSpec := &vitess.PartitionSpec{
|
||||
Action: actionStr,
|
||||
Definitions: []*vitess.PartitionDefinition{partitionDef},
|
||||
}
|
||||
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
PartitionSpecs: []*vitess.PartitionSpec{partitionSpec},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableComputed converts a tree.AlterTableComputed instance into an equivalent CREATE/ALTER SEQUENCE statement.
|
||||
func nodeAlterTableComputed(ctx *Context, tableName tree.TableName, node *tree.AlterTableComputed) (vitess.Statement, error) {
|
||||
if len(node.Defs) > 0 {
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
var persistence tree.Persistence
|
||||
var seqName tree.TableName
|
||||
var trimmedOptions []tree.SequenceOption
|
||||
switch def := node.AddDefs.(type) {
|
||||
case *tree.ColumnComputedDef:
|
||||
if def.Expr != nil {
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
trimmedOptions = make([]tree.SequenceOption, 0, len(def.Options)+2)
|
||||
for _, opt := range def.Options {
|
||||
switch opt.Name {
|
||||
case tree.SeqOptOwnedBy:
|
||||
return NotYetSupportedError("OWNED BY is invalid here")
|
||||
case tree.SeqOptRestart:
|
||||
return NotYetSupportedError("RESTART is not yet supported")
|
||||
case tree.SeqOptName:
|
||||
seqName = opt.SeqName
|
||||
case tree.SeqOptLogged:
|
||||
persistence = tree.PersistencePermanent
|
||||
case tree.SeqOptUnlogged:
|
||||
persistence = tree.PersistenceUnlogged
|
||||
default:
|
||||
trimmedOptions = append(trimmedOptions, opt)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
trimmedOptions = append(trimmedOptions,
|
||||
tree.SequenceOption{
|
||||
Name: tree.SeqOptOwnedBy,
|
||||
ColumnItemVal: tree.NewColumnItem(&tableName, node.Column),
|
||||
},
|
||||
tree.SequenceOption{
|
||||
Name: tree.SeqOptViaAlterTable,
|
||||
},
|
||||
)
|
||||
return nodeCreateSequence(ctx, &tree.CreateSequence{
|
||||
IfNotExists: false,
|
||||
Name: seqName,
|
||||
Persistence: persistence,
|
||||
Options: trimmedOptions,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user