Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
module github.com/dolthub/doltgresql

go 1.25.3
go 1.25.6

require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a
github.com/cockroachdb/errors v1.7.5
github.com/dolthub/dolt/go v0.40.5-0.20260122084121-6b5d5373d1ec
github.com/dolthub/dolt/go v0.40.5-0.20260205001014-db7263eb669c
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
github.com/dolthub/go-mysql-server v0.20.1-0.20260121234050-2f0507726303
github.com/dolthub/go-mysql-server v0.20.1-0.20260204193159-86990113e4cc
github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216
github.com/dolthub/vitess v0.0.0-20260121194826-a5ce52b608e4
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b
github.com/fatih/color v1.13.0
github.com/goccy/go-json v0.10.2
github.com/gogo/protobuf v1.3.2
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:I
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo=
github.com/dolthub/dolt-mcp v0.2.2 h1:bpROmam74n95uU4EA3BpOIVlTDT0pzeFMBwe/YRq2mI=
github.com/dolthub/dolt-mcp v0.2.2/go.mod h1:S++DJ4QWTAXq+0TNzFa7Oq3IhoT456DJHwAINFAHgDQ=
github.com/dolthub/dolt/go v0.40.5-0.20260122084121-6b5d5373d1ec h1:IzUyIbG7oX3Je53nxq0ZNrsp3C9n2THmiWMJHA9KeDo=
github.com/dolthub/dolt/go v0.40.5-0.20260122084121-6b5d5373d1ec/go.mod h1:UUKnXBHOBTr5CzxFLOZ/9Pm0O+F4ZArKRxs5kUMpXH8=
github.com/dolthub/dolt/go v0.40.5-0.20260205001014-db7263eb669c h1:lrTKtYUO5T5ka0/rgOx9TnSIFuzUv6R86wx7cRDVuUU=
github.com/dolthub/dolt/go v0.40.5-0.20260205001014-db7263eb669c/go.mod h1:GTxR5JEXqpGgYHQc6nBnu/m+TgEC/LP9IPB+b3tIrvc=
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca h1:BGFz/0OlKIuC6qHIZQbvPapFvdAJkeEyGXWVgL5clmE=
github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca/go.mod h1:CoDLfgPqHyBtth0Cp+fi/CmC4R81zJNX4wPjShdZ+Bw=
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww=
Expand All @@ -238,8 +238,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U=
github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc=
github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
github.com/dolthub/go-mysql-server v0.20.1-0.20260121234050-2f0507726303 h1:O4s+FF9G1Jv7uRnR/bB9jTzvPfvnZbcZOZMa3cip8d4=
github.com/dolthub/go-mysql-server v0.20.1-0.20260121234050-2f0507726303/go.mod h1:7L2EdzgWLnS7blMNuF+67RTZVMhRLyKQv36mZcjU8u8=
github.com/dolthub/go-mysql-server v0.20.1-0.20260204193159-86990113e4cc h1:gnUcLBhmGmUeoYTdkPuknx2X5Imjo1/O4onz3duwC90=
github.com/dolthub/go-mysql-server v0.20.1-0.20260204193159-86990113e4cc/go.mod h1:LEWdXw6LKjdonOv2X808RpUc8wZVtQx4ZEPvmDWkvY4=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE=
Expand All @@ -250,8 +250,8 @@ github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1 h1:GY17cGA4
github.com/dolthub/pg_query_go/v6 v6.0.0-20251215122834-fb20be4254d1/go.mod h1:qnrZP3/1slFl2Bq5yw38HLOsArZareGwdpEceriblLc=
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 h1:JWkKRE4EHUcEVQCMRBej8DYxjYjRz/9MdF/NNQh0o70=
github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216/go.mod h1:e/FIZVvT2IR53HBCAo41NjqgtEnjMJGKca3Y/dAmZaA=
github.com/dolthub/vitess v0.0.0-20260121194826-a5ce52b608e4 h1:isOZRx9OfOdwZ4UmeYrS+UYiiu02e8taNJLAuqZjfcQ=
github.com/dolthub/vitess v0.0.0-20260121194826-a5ce52b608e4/go.mod h1:FLWqdXsAeeBQyFwDjmBVu0GnbjI2MKeRf3tRVdJEKlI=
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b h1:B8QS0U5EHtJTiOptjti1cH/OiE6uczyhePtvVFigf3w=
github.com/dolthub/vitess v0.0.0-20260202234501-b14ed9b1632b/go.mod h1:eLLslh1CSPMf89pPcaMG4yM72PQbTN9OUYJeAy0fAis=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
Expand Down
2 changes: 1 addition & 1 deletion postgres/parser/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -13073,7 +13073,7 @@ d_expr:
| '(' a_expr ')' '.' '@' ICONST
{
idx, err := $6.numVal().AsInt32()
if err != nil || idx <= 0 { return setErr(sqllex, err) }
if err != nil { return setErr(sqllex, err) }
$$.val = &tree.ColumnAccessExpr{Expr: $2.expr(), ByIndex: true, ColIndex: int(idx-1)}
}
| '(' a_expr ')'
Expand Down
33 changes: 23 additions & 10 deletions server/analyzer/resolve_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package analyzer

import (
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/analyzer"
"github.com/dolthub/go-mysql-server/sql/plan"
Expand All @@ -24,7 +25,7 @@ import (

"github.com/dolthub/doltgresql/core"
"github.com/dolthub/doltgresql/core/id"
"github.com/dolthub/doltgresql/server/expression"
pgexprs "github.com/dolthub/doltgresql/server/expression"
pgtransform "github.com/dolthub/doltgresql/server/transform"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
Expand Down Expand Up @@ -118,26 +119,38 @@ func ResolveTypeForNodes(ctx *sql.Context, a *analyzer.Analyzer, node sql.Node,

// ResolveTypeForExprs replaces types.ResolvableType to appropriate pgtypes.DoltgresType.
func ResolveTypeForExprs(ctx *sql.Context, a *analyzer.Analyzer, node sql.Node, scope *plan.Scope, selector analyzer.RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {
return pgtransform.NodeExprsWithOpaque(node, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
var same = transform.SameTree
switch e := expr.(type) {
case *expression.ExplicitCast:
if rt, ok := e.Type().(*pgtypes.DoltgresType); ok && !rt.IsResolvedType() {
return pgtransform.NodeExprsWithOpaque(node, func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
switch expr := e.(type) {
case *pgexprs.ColumnAccess:
exprType, _ := expr.Type().(*pgtypes.DoltgresType)
if exprType == nil {
return nil, transform.NewTree, errors.New("column access is missing its child expression")
} else if exprType.IsResolvedType() {
// The type has already been resolved
return expr, transform.SameTree, nil
}
newType, err := resolveType(ctx, exprType)
if err != nil {
return nil, transform.NewTree, err
}
return expr.WithType(newType), transform.NewTree, nil
case *pgexprs.ExplicitCast:
if rt, ok := expr.Type().(*pgtypes.DoltgresType); ok && !rt.IsResolvedType() {
dt, err := resolveType(ctx, rt)
if err != nil {
return nil, transform.NewTree, err
}
same = transform.NewTree
if !dt.IsDefined {
return nil, transform.NewTree, pgtypes.ErrTypeIsOnlyAShell.New(dt.Name())
} else {
expr = e.WithCastToType(dt)
return expr.WithCastToType(dt), transform.NewTree, nil
}
} else {
return expr, transform.SameTree, nil
}
return expr, same, nil
default:
// TODO: add expressions that use unresolved types like domain
return e, transform.SameTree, nil
return expr, transform.SameTree, nil
}
})
}
Expand Down
13 changes: 12 additions & 1 deletion server/ast/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,18 @@ func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
logrus.Warnf("collate is not yet supported, ignoring")
return nodeExpr(ctx, node.Expr)
case *tree.ColumnAccessExpr:
return nil, errors.Errorf("(E).x is not yet supported")
colAccess, err := pgexprs.NewColumnAccess(node.ColName, node.ColIndex)
if err != nil {
return nil, err
}
expr, err := nodeExpr(ctx, node.Expr)
if err != nil {
return nil, err
}
return vitess.InjectedExpr{
Expression: colAccess,
Children: vitess.Exprs{expr},
}, nil
case *tree.ColumnItem:
var tableName vitess.TableName
if node.TableName != nil {
Expand Down
184 changes: 184 additions & 0 deletions server/expression/column_access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// 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 (
"fmt"

"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
vitess "github.com/dolthub/vitess/go/vt/sqlparser"

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().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().String())
}
}
return recordVals[expr.colNameIdx].Value, nil
}

// IsNullable implements the sql.Expression interface.
func (expr *ColumnAccess) IsNullable() 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() sql.Type {
if expr.colTyp != nil {
return expr.colTyp
}
if expr.child == nil {
return nil
}
// We're technically returning a different type here since an unresolved type is not the same as a resolved one.
// However, for many early analyzer steps, we only check the ID, so this at least lets us get past those cases.
return pgtypes.NewUnresolvedDoltgresTypeFromID(expr.child.Type().(*pgtypes.DoltgresType).CompositeAttrs[expr.colNameIdx].TypeID)
}

// WithChildren implements the sql.Expression interface.
func (expr *ColumnAccess) WithChildren(children ...sql.Expression) (sql.Expression, error) {
if len(children) != 1 {
return nil, sql.ErrInvalidChildrenNumber.New(expr, len(children), 1)
}
childType := children[0].Type()
doltgresType, ok := childType.(*pgtypes.DoltgresType)
if !ok {
return nil, errors.New("column access is only valid for Doltgres types")
}
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, children[0].Type().String())
} else {
return nil, errors.Errorf("column notation .@%d applied to type %s, which is not a composite type",
expr.colNameIdx+1, children[0].Type().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, children[0].Type().String())
}
idx = expr.colNameIdx
}
return &ColumnAccess{
colName: expr.colName,
colNameIdx: idx,
colTyp: expr.colTyp,
child: children[0],
}, nil
}

// WithResolvedChildren implements the vitess.InjectableExpression interface.
func (expr *ColumnAccess) WithResolvedChildren(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(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
}
53 changes: 53 additions & 0 deletions server/functions/dolt_recordtrim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2024 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 functions

import (
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"

"github.com/dolthub/doltgresql/server/functions/framework"
pgtypes "github.com/dolthub/doltgresql/server/types"
)

// initDoltRecordTrim registers the functions to the catalog.
func initDoltRecordTrim() {
framework.RegisterFunction(dolt_recordTrim)
}

// dolt_recordTrim is used to remove a specific column within a composite type. This will generally lead to an invalid
// value for the composite type, however this is used within the DROP COLUMN table hook to fix data for all columns that
// reference the type, as that is the only time when the data is invalid. This is why this is a "dolt_" function as
// well, as it's not intended for general use.
var dolt_recordTrim = framework.Function2{
Name: "dolt_recordtrim",
Return: pgtypes.AnyElement,
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyElement, pgtypes.Int32},
Strict: true,
Callable: func(ctx *sql.Context, types [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
if !types[0].IsCompositeType() {
return val1, nil
}
trimVal := val2.(int32)
recordVals := val1.([]pgtypes.RecordValue)
if trimVal < 0 || int(trimVal) >= len(recordVals) {
return nil, errors.New("record trim index is out of bounds")
}
newRecordVals := make([]pgtypes.RecordValue, len(recordVals)-1)
copy(newRecordVals, recordVals[:trimVal])
copy(newRecordVals[trimVal:], recordVals[trimVal+1:])
return newRecordVals, nil
},
}
Loading