github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rename_table.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/kv"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/privilege"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    22  	"github.com/cockroachdb/cockroach/pkg/util/log"
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  type renameTableNode struct {
    27  	n            *tree.RenameTable
    28  	oldTn, newTn *tree.TableName
    29  	tableDesc    *sqlbase.MutableTableDescriptor
    30  }
    31  
    32  // RenameTable renames the table, view or sequence.
    33  // Privileges: DROP on source table/view/sequence, CREATE on destination database.
    34  //   Notes: postgres requires the table owner.
    35  //          mysql requires ALTER, DROP on the original table, and CREATE, INSERT
    36  //          on the new table (and does not copy privileges over).
    37  func (p *planner) RenameTable(ctx context.Context, n *tree.RenameTable) (planNode, error) {
    38  	oldTn := n.Name.ToTableName()
    39  	newTn := n.NewName.ToTableName()
    40  	toRequire := resolver.ResolveRequireTableOrViewDesc
    41  	if n.IsView {
    42  		toRequire = resolver.ResolveRequireViewDesc
    43  	} else if n.IsSequence {
    44  		toRequire = resolver.ResolveRequireSequenceDesc
    45  	}
    46  
    47  	tableDesc, err := p.ResolveMutableTableDescriptor(ctx, &oldTn, !n.IfExists, toRequire)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	if tableDesc == nil {
    52  		// Noop.
    53  		return newZeroNode(nil /* columns */), nil
    54  	}
    55  
    56  	if tableDesc.State != sqlbase.TableDescriptor_PUBLIC {
    57  		return nil, sqlbase.NewUndefinedRelationError(&oldTn)
    58  	}
    59  
    60  	if err := p.CheckPrivilege(ctx, tableDesc, privilege.DROP); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Check if any views depend on this table/view. Because our views
    65  	// are currently just stored as strings, they explicitly specify the name
    66  	// of everything they depend on. Rather than trying to rewrite the view's
    67  	// query with the new name, we simply disallow such renames for now.
    68  	if len(tableDesc.DependedOnBy) > 0 {
    69  		return nil, p.dependentViewRenameError(
    70  			ctx, tableDesc.TypeName(), oldTn.String(), tableDesc.ParentID, tableDesc.DependedOnBy[0].ID)
    71  	}
    72  
    73  	return &renameTableNode{n: n, oldTn: &oldTn, newTn: &newTn, tableDesc: tableDesc}, nil
    74  }
    75  
    76  // ReadingOwnWrites implements the planNodeReadingOwnWrites interface.
    77  // This is because RENAME DATABASE performs multiple KV operations on descriptors
    78  // and expects to see its own writes.
    79  func (n *renameTableNode) ReadingOwnWrites() {}
    80  
    81  func (n *renameTableNode) startExec(params runParams) error {
    82  	p := params.p
    83  	ctx := params.ctx
    84  	oldTn := n.oldTn
    85  	newTn := n.newTn
    86  	tableDesc := n.tableDesc
    87  
    88  	oldUn := oldTn.ToUnresolvedObjectName()
    89  	prevDbDesc, prefix, err := p.ResolveUncachedDatabase(ctx, oldUn)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	oldTn.ObjectNamePrefix = prefix
    94  
    95  	// Check if target database exists.
    96  	// We also look at uncached descriptors here.
    97  	newUn := newTn.ToUnresolvedObjectName()
    98  	targetDbDesc, prefix, err := p.ResolveUncachedDatabase(ctx, newUn)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	newTn.ObjectNamePrefix = prefix
   103  
   104  	if err := p.CheckPrivilege(ctx, targetDbDesc, privilege.CREATE); err != nil {
   105  		return err
   106  	}
   107  
   108  	// oldTn and newTn are already normalized, so we can compare directly here.
   109  	if oldTn.Catalog() == newTn.Catalog() &&
   110  		oldTn.Schema() == newTn.Schema() &&
   111  		oldTn.Table() == newTn.Table() {
   112  		// Noop.
   113  		return nil
   114  	}
   115  
   116  	tableDesc.SetName(newTn.Table())
   117  	tableDesc.ParentID = targetDbDesc.ID
   118  
   119  	newTbKey := sqlbase.MakePublicTableNameKey(ctx, params.ExecCfg().Settings,
   120  		targetDbDesc.ID, newTn.Table()).Key(p.ExecCfg().Codec)
   121  
   122  	if err := tableDesc.Validate(ctx, p.txn, p.ExecCfg().Codec); err != nil {
   123  		return err
   124  	}
   125  
   126  	descID := tableDesc.GetID()
   127  	parentSchemaID := tableDesc.GetParentSchemaID()
   128  
   129  	renameDetails := sqlbase.TableDescriptor_NameInfo{
   130  		ParentID:       prevDbDesc.ID,
   131  		ParentSchemaID: parentSchemaID,
   132  		Name:           oldTn.Table()}
   133  	tableDesc.DrainingNames = append(tableDesc.DrainingNames, renameDetails)
   134  	if err := p.writeSchemaChange(
   135  		ctx, tableDesc, sqlbase.InvalidMutationID, tree.AsStringWithFQNames(n.n, params.Ann()),
   136  	); err != nil {
   137  		return err
   138  	}
   139  
   140  	// We update the descriptor to the new name, but also leave the mapping of the
   141  	// old name to the id, so that the name is not reused until the schema changer
   142  	// has made sure it's not in use any more.
   143  	b := &kv.Batch{}
   144  	if p.extendedEvalCtx.Tracing.KVTracingEnabled() {
   145  		log.VEventf(ctx, 2, "CPut %s -> %d", newTbKey, descID)
   146  	}
   147  	err = catalogkv.WriteDescToBatch(ctx, p.extendedEvalCtx.Tracing.KVTracingEnabled(),
   148  		p.EvalContext().Settings, b, p.ExecCfg().Codec, descID, tableDesc.TableDesc())
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	exists, id, err := sqlbase.LookupPublicTableID(
   154  		params.ctx, params.p.txn, p.ExecCfg().Codec, targetDbDesc.ID, newTn.Table(),
   155  	)
   156  	if err == nil && exists {
   157  		// Try and see what kind of object we collided with.
   158  		desc, err := catalogkv.GetDescriptorByID(params.ctx, params.p.txn, p.ExecCfg().Codec, id)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		return makeObjectAlreadyExistsError(desc, newTn.Table())
   163  	} else if err != nil {
   164  		return err
   165  	}
   166  
   167  	b.CPut(newTbKey, descID, nil)
   168  	return p.txn.Run(ctx, b)
   169  }
   170  
   171  func (n *renameTableNode) Next(runParams) (bool, error) { return false, nil }
   172  func (n *renameTableNode) Values() tree.Datums          { return tree.Datums{} }
   173  func (n *renameTableNode) Close(context.Context)        {}
   174  
   175  // TODO(a-robinson): Support renaming objects depended on by views once we have
   176  // a better encoding for view queries (#10083).
   177  func (p *planner) dependentViewRenameError(
   178  	ctx context.Context, typeName, objName string, parentID, viewID sqlbase.ID,
   179  ) error {
   180  	viewDesc, err := sqlbase.GetTableDescFromID(ctx, p.txn, p.ExecCfg().Codec, viewID)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	viewName := viewDesc.Name
   185  	if viewDesc.ParentID != parentID {
   186  		var err error
   187  		viewName, err = p.getQualifiedTableName(ctx, viewDesc)
   188  		if err != nil {
   189  			log.Warningf(ctx, "unable to retrieve name of view %d: %v", viewID, err)
   190  			return sqlbase.NewDependentObjectErrorf(
   191  				"cannot rename %s %q because a view depends on it",
   192  				typeName, objName)
   193  		}
   194  	}
   195  	return errors.WithHintf(
   196  		sqlbase.NewDependentObjectErrorf("cannot rename %s %q because view %q depends on it",
   197  			typeName, objName, viewName),
   198  		"you can drop %s instead.", viewName)
   199  }