github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/procedure_resolved_table.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  	"github.com/dolthub/go-mysql-server/sql"
    19  	"github.com/dolthub/go-mysql-server/sql/transform"
    20  )
    21  
    22  // ProcedureResolvedTable represents a resolved SQL Table inside of a stored procedure. These are initially resolved to
    23  // verify that they exist, and are then reloaded when another statement accesses its data. Some integrators return a
    24  // snapshot of a table during the analysis step as an internal optimization, which is incompatible with stored
    25  // procedures as they require the latest data at each statement.
    26  type ProcedureResolvedTable struct {
    27  	ResolvedTable *ResolvedTable
    28  }
    29  
    30  var _ sql.Node = (*ProcedureResolvedTable)(nil)
    31  var _ sql.DebugStringer = (*ProcedureResolvedTable)(nil)
    32  var _ sql.TableWrapper = (*ProcedureResolvedTable)(nil)
    33  var _ sql.Table = (*ProcedureResolvedTable)(nil)
    34  var _ sql.CollationCoercible = (*ProcedureResolvedTable)(nil)
    35  
    36  // NewProcedureResolvedTable returns a *ProcedureResolvedTable.
    37  func NewProcedureResolvedTable(rt *ResolvedTable) *ProcedureResolvedTable {
    38  	return &ProcedureResolvedTable{rt}
    39  }
    40  
    41  // Resolved implements the sql.Node interface.
    42  func (t *ProcedureResolvedTable) Resolved() bool {
    43  	return t.ResolvedTable.Resolved()
    44  }
    45  
    46  func (t *ProcedureResolvedTable) IsReadOnly() bool {
    47  	return true
    48  }
    49  
    50  // String implements the sql.Node interface.
    51  func (t *ProcedureResolvedTable) String() string {
    52  	return t.ResolvedTable.String()
    53  }
    54  
    55  // Schema implements the sql.Node interface.
    56  func (t *ProcedureResolvedTable) Schema() sql.Schema {
    57  	return t.ResolvedTable.Schema()
    58  }
    59  
    60  // Collation implements the sql.Table interface.
    61  func (t *ProcedureResolvedTable) Collation() sql.CollationID {
    62  	return t.ResolvedTable.Collation()
    63  }
    64  
    65  // Comment implements the sql.CommentedTable interface.
    66  func (t *ProcedureResolvedTable) Comment() string {
    67  	return t.ResolvedTable.Comment()
    68  }
    69  
    70  // DebugString implements the sql.DebugStringer interface.
    71  func (t *ProcedureResolvedTable) DebugString() string {
    72  	return sql.DebugString(t.ResolvedTable)
    73  }
    74  
    75  // Children implements the sql.Node interface.
    76  func (t *ProcedureResolvedTable) Children() []sql.Node {
    77  	return []sql.Node{t.ResolvedTable}
    78  }
    79  
    80  // WithChildren implements the sql.Node interface.
    81  func (t *ProcedureResolvedTable) WithChildren(children ...sql.Node) (sql.Node, error) {
    82  	if len(children) != 1 {
    83  		return nil, sql.ErrInvalidChildrenNumber.New(t, len(children), 1)
    84  	}
    85  	// Even though we return the *ResolvedTable in Children, we cannot assume that the given child is still
    86  	// *ResolvedTable. In the analyzer, there are instances where the table is buried under other nodes such as
    87  	// tracking nodes, so we must walk the tree and find the table.
    88  	nt, _, err := transform.Node(children[0], func(n sql.Node) (sql.Node, transform.TreeIdentity, error) {
    89  		rt, ok := children[0].(*ResolvedTable)
    90  		if !ok {
    91  			return n, transform.SameTree, nil
    92  		}
    93  		return NewProcedureResolvedTable(rt), transform.NewTree, nil
    94  	})
    95  	return nt, err
    96  }
    97  
    98  // CheckPrivileges implements the interface sql.Node.
    99  func (t *ProcedureResolvedTable) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool {
   100  	return t.ResolvedTable.CheckPrivileges(ctx, opChecker)
   101  }
   102  
   103  // CollationCoercibility implements the interface sql.CollationCoercible.
   104  func (t *ProcedureResolvedTable) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   105  	return t.ResolvedTable.CollationCoercibility(ctx)
   106  }
   107  
   108  // Underlying implements the sql.TableWrapper interface.
   109  func (t *ProcedureResolvedTable) Underlying() sql.Table {
   110  	return t.ResolvedTable.Table
   111  }
   112  
   113  // Name implements the sql.Table interface.
   114  func (t *ProcedureResolvedTable) Name() string {
   115  	return t.ResolvedTable.Name()
   116  }
   117  
   118  // Partitions implements the sql.Table interface.
   119  func (t *ProcedureResolvedTable) Partitions(ctx *sql.Context) (sql.PartitionIter, error) {
   120  	rt, err := t.NewestTable(ctx)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	return rt.Partitions(ctx)
   125  }
   126  
   127  // PartitionRows implements the sql.Table interface.
   128  func (t *ProcedureResolvedTable) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) {
   129  	rt, err := t.NewestTable(ctx)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return rt.PartitionRows(ctx, partition)
   134  }
   135  
   136  // NewestTable fetches the newest copy of the contained table from the database.
   137  func (t *ProcedureResolvedTable) NewestTable(ctx *sql.Context) (*ResolvedTable, error) {
   138  	// If no database was given, such as with the "dual" table, then we return the given table as-is.
   139  	if t.ResolvedTable.SqlDatabase == nil {
   140  		return t.ResolvedTable, nil
   141  	}
   142  
   143  	if IsDualTable(t.ResolvedTable) {
   144  		return t.ResolvedTable, nil
   145  	} else if t.ResolvedTable.AsOf == nil {
   146  		tbl, ok, err := t.ResolvedTable.SqlDatabase.GetTableInsensitive(ctx, t.ResolvedTable.Table.Name())
   147  		if err != nil {
   148  			return nil, err
   149  		} else if !ok {
   150  			return nil, sql.ErrTableNotFound.New(t.ResolvedTable.Table.Name())
   151  		}
   152  		rt, err := t.ResolvedTable.ReplaceTable(tbl)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		return rt.(*ResolvedTable), nil
   157  	} else {
   158  		versionedDb, ok := t.ResolvedTable.SqlDatabase.(sql.VersionedDatabase)
   159  		if !ok {
   160  			return nil, sql.ErrAsOfNotSupported.New(t.ResolvedTable.SqlDatabase.Name())
   161  		}
   162  
   163  		tbl, ok, err := versionedDb.GetTableInsensitiveAsOf(ctx, t.ResolvedTable.Table.Name(), t.ResolvedTable.AsOf)
   164  		if err != nil {
   165  			return nil, err
   166  		} else if !ok {
   167  			return nil, sql.ErrTableNotFound.New(t.ResolvedTable.Table.Name())
   168  		}
   169  		rt, err := t.ResolvedTable.ReplaceTable(tbl)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		return rt.(*ResolvedTable), nil
   174  	}
   175  }