github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/alter_check.go (about)

     1  // Copyright 2021 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package plan
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"gopkg.in/src-d/go-errors.v1"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/dolthub/go-mysql-server/sql/expression"
    24  	"github.com/dolthub/go-mysql-server/sql/transform"
    25  )
    26  
    27  var (
    28  	// ErrNoCheckConstraintSupport is returned when the table does not support CONSTRAINT CHECK operations.
    29  	ErrNoCheckConstraintSupport = errors.NewKind("the table does not support check constraint operations: %s")
    30  
    31  	// ErrCheckViolated is returned when the check constraint evaluates to false
    32  	ErrCheckViolated = errors.NewKind("check constraint %s is violated.")
    33  )
    34  
    35  type CreateCheck struct {
    36  	ddlNode
    37  	Table *ResolvedTable
    38  	Check *sql.CheckConstraint
    39  }
    40  
    41  var _ sql.Node = (*CreateCheck)(nil)
    42  var _ sql.CollationCoercible = (*CreateCheck)(nil)
    43  
    44  type DropCheck struct {
    45  	ddlNode
    46  	Table *ResolvedTable
    47  	Name  string
    48  }
    49  
    50  var _ sql.Node = (*DropCheck)(nil)
    51  var _ sql.CollationCoercible = (*DropCheck)(nil)
    52  
    53  func NewAlterAddCheck(table *ResolvedTable, check *sql.CheckConstraint) *CreateCheck {
    54  	return &CreateCheck{
    55  		ddlNode: ddlNode{table.SqlDatabase},
    56  		Table:   table,
    57  		Check:   check,
    58  	}
    59  }
    60  
    61  func NewAlterDropCheck(table *ResolvedTable, name string) *DropCheck {
    62  	return &DropCheck{
    63  		ddlNode: ddlNode{table.SqlDatabase},
    64  		Table:   table,
    65  		Name:    name,
    66  	}
    67  }
    68  
    69  // Expressions implements the sql.Expressioner interface.
    70  func (c *CreateCheck) Expressions() []sql.Expression {
    71  	return []sql.Expression{c.Check.Expr}
    72  }
    73  
    74  // Resolved implements the Resolvable interface.
    75  func (c *CreateCheck) Resolved() bool {
    76  	return c.Check.Expr.Resolved()
    77  }
    78  
    79  func (c *CreateCheck) IsReadOnly() bool {
    80  	return false
    81  }
    82  
    83  // WithExpressions implements the sql.Expressioner interface.
    84  func (c *CreateCheck) WithExpressions(exprs ...sql.Expression) (sql.Node, error) {
    85  	if len(exprs) != 1 {
    86  		return nil, fmt.Errorf("expected one expression, got: %d", len(exprs))
    87  	}
    88  
    89  	nc := *c
    90  	nc.Check.Expr = exprs[0]
    91  	return &nc, nil
    92  }
    93  
    94  // WithChildren implements the Node interface.
    95  func (c *CreateCheck) WithChildren(children ...sql.Node) (sql.Node, error) {
    96  	if len(children) != 1 {
    97  		return nil, sql.ErrInvalidChildrenNumber.New(c, len(children), 1)
    98  	}
    99  	return NewAlterAddCheck(children[0].(*ResolvedTable), c.Check), nil
   100  }
   101  
   102  func (c *CreateCheck) Children() []sql.Node {
   103  	return []sql.Node{c.Table}
   104  }
   105  
   106  // CheckPrivileges implements the interface sql.Node.
   107  func (c *CreateCheck) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   108  	db := c.Table.Database()
   109  	subject := sql.PrivilegeCheckSubject{
   110  		Database: CheckPrivilegeNameForDatabase(db),
   111  		Table:    getTableName(c.Table),
   112  	}
   113  	return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Alter))
   114  }
   115  
   116  // CollationCoercibility implements the interface sql.CollationCoercible.
   117  func (c *CreateCheck) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   118  	return sql.Collation_binary, 7
   119  }
   120  
   121  func (c *CreateCheck) Schema() sql.Schema { return nil }
   122  
   123  func (c CreateCheck) String() string {
   124  	pr := sql.NewTreePrinter()
   125  	_ = pr.WriteNode("AddCheck(%s)", c.Check.Name)
   126  	_ = pr.WriteChildren(
   127  		fmt.Sprintf("Table(%s)", c.Table.Name()),
   128  		fmt.Sprintf("Expr(%s)", c.Check.Expr.String()),
   129  	)
   130  	return pr.String()
   131  }
   132  
   133  func (d *DropCheck) Children() []sql.Node {
   134  	return []sql.Node{d.Table}
   135  }
   136  
   137  // WithChildren implements the Node interface.
   138  func (d *DropCheck) WithChildren(children ...sql.Node) (sql.Node, error) {
   139  	if len(children) != 1 {
   140  		return nil, sql.ErrInvalidChildrenNumber.New(d, len(children), 1)
   141  	}
   142  	return NewAlterDropCheck(children[0].(*ResolvedTable), d.Name), nil
   143  }
   144  
   145  // CheckPrivileges implements the interface sql.Node.
   146  func (d *DropCheck) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   147  	db := d.Table.Database()
   148  	subject := sql.PrivilegeCheckSubject{
   149  		Database: CheckPrivilegeNameForDatabase(db),
   150  		Table:    getTableName(d.Table),
   151  	}
   152  
   153  	return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Alter))
   154  }
   155  
   156  // CollationCoercibility implements the interface sql.CollationCoercible.
   157  func (d *DropCheck) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   158  	return sql.Collation_binary, 7
   159  }
   160  
   161  func (d *DropCheck) Schema() sql.Schema { return nil }
   162  
   163  func (d *DropCheck) IsReadOnly() bool { return false }
   164  
   165  func (d DropCheck) String() string {
   166  	pr := sql.NewTreePrinter()
   167  	_ = pr.WriteNode("DropCheck(%s)", d.Name)
   168  	_ = pr.WriteChildren(fmt.Sprintf("Table(%s)", d.Table.Name()))
   169  	return pr.String()
   170  }
   171  
   172  func NewCheckDefinition(ctx *sql.Context, check *sql.CheckConstraint) (*sql.CheckDefinition, error) {
   173  	// When transforming an analyzed CheckConstraint into a CheckDefinition (for storage), we strip off any table
   174  	// qualifiers that got resolved during analysis. This is to naively match the MySQL behavior, which doesn't print
   175  	// any table qualifiers in check expressions.
   176  	unqualifiedCols, _, err := transform.Expr(check.Expr, func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
   177  		gf, ok := e.(*expression.GetField)
   178  		if ok {
   179  			return expression.NewGetField(gf.Index(), gf.Type(), gf.Name(), gf.IsNullable()), transform.NewTree, nil
   180  		}
   181  		return e, transform.SameTree, nil
   182  	})
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	return &sql.CheckDefinition{
   188  		Name:            check.Name,
   189  		CheckExpression: fmt.Sprintf("%s", unqualifiedCols),
   190  		Enforced:        check.Enforced,
   191  	}, nil
   192  }
   193  
   194  // DropConstraint is a temporary node to handle dropping a named constraint on a table. The type of the constraint is
   195  // not known, and is determined during analysis.
   196  type DropConstraint struct {
   197  	UnaryNode
   198  	Name string
   199  }
   200  
   201  var _ sql.Node = (*DropConstraint)(nil)
   202  var _ sql.CollationCoercible = (*DropConstraint)(nil)
   203  
   204  func (d *DropConstraint) String() string {
   205  	tp := sql.NewTreePrinter()
   206  	_ = tp.WriteNode("DropConstraint(%s)", d.Name)
   207  	_ = tp.WriteChildren(d.UnaryNode.Child.String())
   208  	return tp.String()
   209  }
   210  
   211  func (d DropConstraint) WithChildren(children ...sql.Node) (sql.Node, error) {
   212  	if len(children) != 1 {
   213  		return nil, sql.ErrInvalidChildrenNumber.New(d, len(children), 1)
   214  	}
   215  
   216  	nd := &d
   217  	nd.UnaryNode = UnaryNode{children[0]}
   218  	return nd, nil
   219  }
   220  
   221  // CheckPrivileges implements the interface sql.Node.
   222  func (d *DropConstraint) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   223  	db := GetDatabase(d.Child)
   224  	subject := sql.PrivilegeCheckSubject{
   225  		Database: CheckPrivilegeNameForDatabase(db),
   226  		Table:    getTableName(d.Child),
   227  	}
   228  	return opChecker.UserHasPrivileges(ctx, sql.NewPrivilegedOperation(subject, sql.PrivilegeType_Alter))
   229  }
   230  
   231  func (d *DropConstraint) IsReadOnly() bool { return false }
   232  
   233  // CollationCoercibility implements the interface sql.CollationCoercible.
   234  func (d *DropConstraint) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   235  	return sql.Collation_binary, 7
   236  }
   237  
   238  // NewDropConstraint returns a new DropConstraint node
   239  func NewDropConstraint(table *UnresolvedTable, name string) *DropConstraint {
   240  	return &DropConstraint{
   241  		UnaryNode: UnaryNode{table},
   242  		Name:      name,
   243  	}
   244  }