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

     1  // Copyright 2019 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 cat
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	"fmt"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    21  	"github.com/cockroachdb/cockroach/pkg/util"
    22  	"github.com/cockroachdb/cockroach/pkg/util/treeprinter"
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  // ExpandDataSourceGlob is a utility function that expands a tree.TablePattern
    27  // into a list of object names.
    28  func ExpandDataSourceGlob(
    29  	ctx context.Context, catalog Catalog, flags Flags, pattern tree.TablePattern,
    30  ) ([]DataSourceName, error) {
    31  
    32  	switch p := pattern.(type) {
    33  	case *tree.TableName:
    34  		_, name, err := catalog.ResolveDataSource(ctx, flags, p)
    35  		if err != nil {
    36  			return nil, err
    37  		}
    38  		return []DataSourceName{name}, nil
    39  
    40  	case *tree.AllTablesSelector:
    41  		schema, _, err := catalog.ResolveSchema(ctx, flags, &p.ObjectNamePrefix)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  
    46  		return schema.GetDataSourceNames(ctx)
    47  
    48  	default:
    49  		return nil, errors.Errorf("invalid TablePattern type %T", p)
    50  	}
    51  }
    52  
    53  // ResolveTableIndex resolves a TableIndexName.
    54  func ResolveTableIndex(
    55  	ctx context.Context, catalog Catalog, flags Flags, name *tree.TableIndexName,
    56  ) (Index, DataSourceName, error) {
    57  	if name.Table.ObjectName != "" {
    58  		ds, tn, err := catalog.ResolveDataSource(ctx, flags, &name.Table)
    59  		if err != nil {
    60  			return nil, DataSourceName{}, err
    61  		}
    62  		table, ok := ds.(Table)
    63  		if !ok {
    64  			return nil, DataSourceName{}, pgerror.Newf(
    65  				pgcode.WrongObjectType, "%q is not a table", name.Table.ObjectName,
    66  			)
    67  		}
    68  		if name.Index == "" {
    69  			// Return primary index.
    70  			return table.Index(0), tn, nil
    71  		}
    72  		for i := 0; i < table.IndexCount(); i++ {
    73  			if idx := table.Index(i); idx.Name() == tree.Name(name.Index) {
    74  				return idx, tn, nil
    75  			}
    76  		}
    77  		return nil, DataSourceName{}, pgerror.Newf(
    78  			pgcode.UndefinedObject, "index %q does not exist", name.Index,
    79  		)
    80  	}
    81  
    82  	// We have to search for a table that has an index with the given name.
    83  	schema, _, err := catalog.ResolveSchema(ctx, flags, &name.Table.ObjectNamePrefix)
    84  	if err != nil {
    85  		return nil, DataSourceName{}, err
    86  	}
    87  	dsNames, err := schema.GetDataSourceNames(ctx)
    88  	if err != nil {
    89  		return nil, DataSourceName{}, err
    90  	}
    91  	var found Index
    92  	var foundTabName DataSourceName
    93  	for i := range dsNames {
    94  		ds, tn, err := catalog.ResolveDataSource(ctx, flags, &dsNames[i])
    95  		if err != nil {
    96  			return nil, DataSourceName{}, err
    97  		}
    98  		table, ok := ds.(Table)
    99  		if !ok {
   100  			// Not a table, ignore.
   101  			continue
   102  		}
   103  		for i := 0; i < table.IndexCount(); i++ {
   104  			if idx := table.Index(i); idx.Name() == tree.Name(name.Index) {
   105  				if found != nil {
   106  					return nil, DataSourceName{}, pgerror.Newf(pgcode.AmbiguousParameter,
   107  						"index name %q is ambiguous (found in %s and %s)",
   108  						name.Index, tn.String(), foundTabName.String())
   109  				}
   110  				found = idx
   111  				foundTabName = tn
   112  				break
   113  			}
   114  		}
   115  	}
   116  	if found == nil {
   117  		return nil, DataSourceName{}, pgerror.Newf(
   118  			pgcode.UndefinedObject, "index %q does not exist", name.Index,
   119  		)
   120  	}
   121  	return found, foundTabName, nil
   122  }
   123  
   124  // ConvertColumnIDsToOrdinals converts a list of ColumnIDs (such as from a
   125  // tree.TableRef), to a list of ordinal positions of columns within the given
   126  // table. See tree.Table for more information on column ordinals.
   127  func ConvertColumnIDsToOrdinals(tab Table, columns []tree.ColumnID) (ordinals []int) {
   128  	ordinals = make([]int, len(columns))
   129  	for i, c := range columns {
   130  		ord := 0
   131  		cnt := tab.ColumnCount()
   132  		for ord < cnt {
   133  			if tab.Column(ord).ColID() == StableID(c) {
   134  				break
   135  			}
   136  			ord++
   137  		}
   138  		if ord >= cnt {
   139  			panic(pgerror.Newf(pgcode.UndefinedColumn,
   140  				"column [%d] does not exist", c))
   141  		}
   142  		ordinals[i] = ord
   143  	}
   144  	return ordinals
   145  }
   146  
   147  // FindTableColumnByName returns the ordinal of the non-mutation column having
   148  // the given name, if one exists in the given table. Otherwise, it returns -1.
   149  func FindTableColumnByName(tab Table, name tree.Name) int {
   150  	for ord, n := 0, tab.ColumnCount(); ord < n; ord++ {
   151  		if tab.Column(ord).ColName() == name {
   152  			return ord
   153  		}
   154  	}
   155  	return -1
   156  }
   157  
   158  // FormatTable nicely formats a catalog table using a treeprinter for debugging
   159  // and testing.
   160  func FormatTable(cat Catalog, tab Table, tp treeprinter.Node) {
   161  	child := tp.Childf("TABLE %s", tab.Name())
   162  	if tab.IsVirtualTable() {
   163  		child.Child("virtual table")
   164  	}
   165  
   166  	var buf bytes.Buffer
   167  	for i := 0; i < tab.DeletableColumnCount(); i++ {
   168  		buf.Reset()
   169  		formatColumn(tab.Column(i), IsMutationColumn(tab, i), &buf)
   170  		child.Child(buf.String())
   171  	}
   172  
   173  	// If we only have one primary family (the default), don't print it.
   174  	if tab.FamilyCount() > 1 || tab.Family(0).Name() != "primary" {
   175  		for i := 0; i < tab.FamilyCount(); i++ {
   176  			buf.Reset()
   177  			formatFamily(tab.Family(i), &buf)
   178  			child.Child(buf.String())
   179  		}
   180  	}
   181  
   182  	for i := 0; i < tab.CheckCount(); i++ {
   183  		child.Childf("CHECK (%s)", tab.Check(i).Constraint)
   184  	}
   185  
   186  	for i := 0; i < tab.DeletableIndexCount(); i++ {
   187  		formatCatalogIndex(tab, i, child)
   188  	}
   189  
   190  	for i := 0; i < tab.OutboundForeignKeyCount(); i++ {
   191  		formatCatalogFKRef(cat, false /* inbound */, tab.OutboundForeignKey(i), child)
   192  	}
   193  
   194  	for i := 0; i < tab.InboundForeignKeyCount(); i++ {
   195  		formatCatalogFKRef(cat, true /* inbound */, tab.InboundForeignKey(i), child)
   196  	}
   197  
   198  	// TODO(radu): show stats.
   199  }
   200  
   201  // formatCatalogIndex nicely formats a catalog index using a treeprinter for
   202  // debugging and testing.
   203  func formatCatalogIndex(tab Table, ord int, tp treeprinter.Node) {
   204  	idx := tab.Index(ord)
   205  	inverted := ""
   206  	if idx.IsInverted() {
   207  		inverted = "INVERTED "
   208  	}
   209  	mutation := ""
   210  	if IsMutationIndex(tab, ord) {
   211  		mutation = " (mutation)"
   212  	}
   213  	child := tp.Childf("%sINDEX %s%s", inverted, idx.Name(), mutation)
   214  
   215  	var buf bytes.Buffer
   216  	colCount := idx.ColumnCount()
   217  	if ord == PrimaryIndex {
   218  		// Omit the "stored" columns from the primary index.
   219  		colCount = idx.KeyColumnCount()
   220  	}
   221  
   222  	for i := 0; i < colCount; i++ {
   223  		buf.Reset()
   224  
   225  		idxCol := idx.Column(i)
   226  		formatColumn(idxCol.Column, false /* isMutationCol */, &buf)
   227  		if idxCol.Descending {
   228  			fmt.Fprintf(&buf, " desc")
   229  		}
   230  
   231  		if i >= idx.LaxKeyColumnCount() {
   232  			fmt.Fprintf(&buf, " (storing)")
   233  		}
   234  
   235  		child.Child(buf.String())
   236  	}
   237  
   238  	FormatZone(idx.Zone(), child)
   239  
   240  	partPrefixes := idx.PartitionByListPrefixes()
   241  	if len(partPrefixes) != 0 {
   242  		c := child.Child("partition by list prefixes")
   243  		for i := range partPrefixes {
   244  			c.Child(partPrefixes[i].String())
   245  		}
   246  	}
   247  	if n := idx.InterleaveAncestorCount(); n > 0 {
   248  		c := child.Child("interleave ancestors")
   249  		for i := 0; i < n; i++ {
   250  			table, index, numKeyCols := idx.InterleaveAncestor(i)
   251  			c.Childf(
   252  				"table=%d index=%d (%d key column%s)",
   253  				table, index, numKeyCols, util.Pluralize(int64(numKeyCols)),
   254  			)
   255  		}
   256  	}
   257  	if n := idx.InterleavedByCount(); n > 0 {
   258  		c := child.Child("interleaved by")
   259  		for i := 0; i < n; i++ {
   260  			table, index := idx.InterleavedBy(i)
   261  			c.Childf("table=%d index=%d", table, index)
   262  		}
   263  	}
   264  }
   265  
   266  // formatColPrefix returns a string representation of a list of columns. The
   267  // columns are provided through a function.
   268  func formatCols(tab Table, numCols int, colOrdinal func(tab Table, i int) int) string {
   269  	var buf bytes.Buffer
   270  	buf.WriteByte('(')
   271  	for i := 0; i < numCols; i++ {
   272  		if i > 0 {
   273  			buf.WriteString(", ")
   274  		}
   275  		colName := tab.Column(colOrdinal(tab, i)).ColName()
   276  		buf.WriteString(colName.String())
   277  	}
   278  	buf.WriteByte(')')
   279  
   280  	return buf.String()
   281  }
   282  
   283  // formatCatalogFKRef nicely formats a catalog foreign key reference using a
   284  // treeprinter for debugging and testing.
   285  func formatCatalogFKRef(
   286  	catalog Catalog, inbound bool, fkRef ForeignKeyConstraint, tp treeprinter.Node,
   287  ) {
   288  	originDS, _, err := catalog.ResolveDataSourceByID(context.TODO(), Flags{}, fkRef.OriginTableID())
   289  	if err != nil {
   290  		panic(err)
   291  	}
   292  	refDS, _, err := catalog.ResolveDataSourceByID(context.TODO(), Flags{}, fkRef.ReferencedTableID())
   293  	if err != nil {
   294  		panic(err)
   295  	}
   296  	title := "CONSTRAINT"
   297  	if inbound {
   298  		title = "REFERENCED BY " + title
   299  	}
   300  	var extra bytes.Buffer
   301  	if fkRef.MatchMethod() != tree.MatchSimple {
   302  		fmt.Fprintf(&extra, " %s", fkRef.MatchMethod())
   303  	}
   304  
   305  	if action := fkRef.DeleteReferenceAction(); action != tree.NoAction {
   306  		fmt.Fprintf(&extra, " ON DELETE %s", action.String())
   307  	}
   308  
   309  	tp.Childf(
   310  		"%s %s FOREIGN KEY %v %s REFERENCES %v %s%s",
   311  		title,
   312  		fkRef.Name(),
   313  		originDS.Name(),
   314  		formatCols(originDS.(Table), fkRef.ColumnCount(), fkRef.OriginColumnOrdinal),
   315  		refDS.Name(),
   316  		formatCols(refDS.(Table), fkRef.ColumnCount(), fkRef.ReferencedColumnOrdinal),
   317  		extra.String(),
   318  	)
   319  }
   320  
   321  func formatColumn(col Column, isMutationCol bool, buf *bytes.Buffer) {
   322  	fmt.Fprintf(buf, "%s %s", col.ColName(), col.DatumType())
   323  	if !col.IsNullable() {
   324  		fmt.Fprintf(buf, " not null")
   325  	}
   326  	if col.IsComputed() {
   327  		fmt.Fprintf(buf, " as (%s) stored", col.ComputedExprStr())
   328  	}
   329  	if col.HasDefault() {
   330  		fmt.Fprintf(buf, " default (%s)", col.DefaultExprStr())
   331  	}
   332  	if col.IsHidden() {
   333  		fmt.Fprintf(buf, " [hidden]")
   334  	}
   335  	if isMutationCol {
   336  		fmt.Fprintf(buf, " [mutation]")
   337  	}
   338  }
   339  
   340  func formatFamily(family Family, buf *bytes.Buffer) {
   341  	fmt.Fprintf(buf, "FAMILY %s (", family.Name())
   342  	for i, n := 0, family.ColumnCount(); i < n; i++ {
   343  		if i != 0 {
   344  			buf.WriteString(", ")
   345  		}
   346  		col := family.Column(i)
   347  		buf.WriteString(string(col.ColName()))
   348  	}
   349  	buf.WriteString(")")
   350  }