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

     1  // Copyright 2018 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/sql/pgwire/pgcode"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    19  )
    20  
    21  // max1RowNode wraps another planNode, returning at most 1 row from the wrapped
    22  // node. If the wrapped node produces more than 1 row, this planNode returns an
    23  // error.
    24  //
    25  // This node is useful for constructing subqueries. Some ways of using
    26  // subqueries in SQL, such as using a subquery as an expression, expect that
    27  // the subquery can return at most 1 row - that expectation must be enforced at
    28  // runtime.
    29  type max1RowNode struct {
    30  	plan planNode
    31  
    32  	nexted    bool
    33  	values    tree.Datums
    34  	errorText string
    35  }
    36  
    37  func (m *max1RowNode) startExec(runParams) error {
    38  	return nil
    39  }
    40  
    41  func (m *max1RowNode) Next(params runParams) (bool, error) {
    42  	if m.nexted {
    43  		return false, nil
    44  	}
    45  	m.nexted = true
    46  
    47  	ok, err := m.plan.Next(params)
    48  	if !ok || err != nil {
    49  		return ok, err
    50  	}
    51  	if ok {
    52  		// We need to eagerly check our parent plan for a new row, to ensure that
    53  		// we return an error as per the contract of this node if the parent plan
    54  		// isn't exhausted after a single row.
    55  		m.values = make(tree.Datums, len(m.plan.Values()))
    56  		copy(m.values, m.plan.Values())
    57  		var secondOk bool
    58  		secondOk, err = m.plan.Next(params)
    59  		if secondOk {
    60  			// TODO(knz): m.errorText could be passed via log.Safe if there
    61  			// was a guarantee that it does not contain PII. Or better yet,
    62  			// the caller would construct an `error` object to return here
    63  			// instead of a string.
    64  			return false, pgerror.Newf(pgcode.CardinalityViolation, "%s", m.errorText)
    65  		}
    66  	}
    67  	return ok, err
    68  }
    69  
    70  func (m *max1RowNode) Values() tree.Datums {
    71  	return m.values
    72  }
    73  
    74  func (m *max1RowNode) Close(ctx context.Context) {
    75  	m.plan.Close(ctx)
    76  }