chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
// Copyright 2026 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package expression
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// ColumnAccess represents an ARRAY[...] expression.
|
||||
type ColumnAccess struct {
|
||||
colName string
|
||||
colNameIdx int
|
||||
colTyp *pgtypes.DoltgresType
|
||||
child sql.Expression
|
||||
}
|
||||
|
||||
var _ vitess.Injectable = (*ColumnAccess)(nil)
|
||||
var _ sql.Expression = (*ColumnAccess)(nil)
|
||||
|
||||
// NewColumnAccess returns a new *ColumnAccess.
|
||||
func NewColumnAccess(colName string, colIdx int) (*ColumnAccess, error) {
|
||||
if len(colName) > 0 {
|
||||
return &ColumnAccess{
|
||||
colName: colName,
|
||||
colNameIdx: -1,
|
||||
colTyp: nil,
|
||||
child: nil,
|
||||
}, nil
|
||||
} else {
|
||||
return &ColumnAccess{
|
||||
colName: "",
|
||||
colNameIdx: colIdx,
|
||||
colTyp: nil,
|
||||
child: nil,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Children implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) Children() []sql.Expression {
|
||||
return []sql.Expression{expr.child}
|
||||
}
|
||||
|
||||
// Eval implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) Eval(ctx *sql.Context, row sql.Row) (any, error) {
|
||||
field, err := expr.child.Eval(ctx, row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == nil {
|
||||
return nil, nil
|
||||
}
|
||||
recordVals, ok := field.([]pgtypes.RecordValue)
|
||||
if !ok {
|
||||
if len(expr.colName) > 0 {
|
||||
return nil, errors.Errorf("column notation .%s applied to type %s, which is not a composite type",
|
||||
expr.colName, expr.child.Type(ctx).String())
|
||||
} else {
|
||||
return nil, errors.Errorf("column notation .@%d applied to type %s, which is not a composite type",
|
||||
expr.colNameIdx+1, expr.child.Type(ctx).String())
|
||||
}
|
||||
}
|
||||
return recordVals[expr.colNameIdx].Value, nil
|
||||
}
|
||||
|
||||
// IsNullable implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) IsNullable(ctx *sql.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Resolved implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) Resolved() bool {
|
||||
return expr.child != nil && expr.child.Resolved()
|
||||
}
|
||||
|
||||
// String implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) String() string {
|
||||
if expr.child == nil {
|
||||
return "COLUMN_ACCESS"
|
||||
}
|
||||
if len(expr.colName) > 0 {
|
||||
return fmt.Sprintf("(%s).%s", expr.child.String(), expr.colName)
|
||||
} else {
|
||||
return fmt.Sprintf("(%s).@%d", expr.child.String(), expr.colNameIdx+1)
|
||||
}
|
||||
}
|
||||
|
||||
// Type implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) Type(ctx *sql.Context) sql.Type {
|
||||
if expr.colTyp != nil {
|
||||
return expr.colTyp
|
||||
}
|
||||
if expr.child == nil {
|
||||
return nil
|
||||
}
|
||||
typ, ok := expr.child.Type(ctx).(*pgtypes.DoltgresType)
|
||||
if !ok {
|
||||
return pgtypes.Unknown
|
||||
}
|
||||
return typ.CompositeAttrs[expr.colNameIdx].Type
|
||||
}
|
||||
|
||||
// WithChildren implements the sql.Expression interface.
|
||||
func (expr *ColumnAccess) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
||||
if len(children) != 1 {
|
||||
return nil, sql.ErrInvalidChildrenNumber.New(expr, len(children), 1)
|
||||
}
|
||||
child := children[0]
|
||||
childType := child.Type(ctx)
|
||||
doltgresType, ok := childType.(*pgtypes.DoltgresType)
|
||||
if !ok {
|
||||
if _, ok := child.(*expression.BindVar); ok {
|
||||
return &ColumnAccess{
|
||||
colName: expr.colName,
|
||||
colNameIdx: expr.colNameIdx,
|
||||
colTyp: expr.colTyp,
|
||||
child: child,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("column access is only valid for Doltgres types")
|
||||
}
|
||||
if !doltgresType.IsResolvedType() {
|
||||
typeColl, err := core.GetTypesCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolvedType, err := typeColl.ResolveType(ctx, doltgresType.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doltgresType = resolvedType
|
||||
}
|
||||
if !doltgresType.IsDefined {
|
||||
return nil, pgtypes.ErrTypeIsOnlyAShell.New(doltgresType.Name())
|
||||
}
|
||||
if !doltgresType.IsCompositeType() {
|
||||
if len(expr.colName) > 0 {
|
||||
return nil, errors.Errorf("column notation .%s applied to type %s, which is not a composite type",
|
||||
expr.colName, child.Type(ctx).String())
|
||||
} else {
|
||||
return nil, errors.Errorf("column notation .@%d applied to type %s, which is not a composite type",
|
||||
expr.colNameIdx+1, child.Type(ctx).String())
|
||||
}
|
||||
}
|
||||
var idx int
|
||||
if len(expr.colName) > 0 {
|
||||
idx = -1
|
||||
for _, attr := range doltgresType.CompositeAttrs {
|
||||
if attr.Name == expr.colName {
|
||||
idx = int(attr.Num - 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx == -1 {
|
||||
return nil, errors.Errorf(`column "%s" not found in data type %s`,
|
||||
expr.colName, doltgresType.String())
|
||||
}
|
||||
} else {
|
||||
if expr.colNameIdx < 0 || expr.colNameIdx >= len(doltgresType.CompositeAttrs) {
|
||||
return nil, errors.Errorf("column notation .@%d applied to type %s is out of bounds",
|
||||
expr.colNameIdx+1, child.Type(ctx).String())
|
||||
}
|
||||
idx = expr.colNameIdx
|
||||
}
|
||||
return &ColumnAccess{
|
||||
colName: expr.colName,
|
||||
colNameIdx: idx,
|
||||
colTyp: doltgresType.CompositeAttrs[idx].Type,
|
||||
child: child,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithResolvedChildren implements the vitess.InjectableExpression interface.
|
||||
func (expr *ColumnAccess) WithResolvedChildren(ctx context.Context, children []any) (any, error) {
|
||||
newExpressions := make([]sql.Expression, len(children))
|
||||
for i, resolvedChild := range children {
|
||||
resolvedExpression, ok := resolvedChild.(sql.Expression)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("expected vitess child to be an expression but has type `%T`", resolvedChild)
|
||||
}
|
||||
newExpressions[i] = resolvedExpression
|
||||
}
|
||||
return expr.WithChildren(ctx.(*sql.Context), newExpressions...)
|
||||
}
|
||||
|
||||
// WithType returns this expression with the given type set, as it must be set within the analyzer.
|
||||
func (expr *ColumnAccess) WithType(typ *pgtypes.DoltgresType) sql.Expression {
|
||||
ne := *expr
|
||||
ne.colTyp = typ
|
||||
return &ne
|
||||
}
|
||||
Reference in New Issue
Block a user