Files
2026-07-13 12:32:25 +08:00

145 lines
4.3 KiB
Go

// Copyright 2023 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 ast
import (
"github.com/cockroachdb/errors"
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
"github.com/dolthub/doltgresql/server/auth"
)
// nodeInsert handles *tree.Insert nodes.
func nodeInsert(ctx *Context, node *tree.Insert) (insert *vitess.Insert, err error) {
if node == nil {
return nil, nil
}
ctx.Auth().PushAuthType(auth.AuthType_INSERT)
defer ctx.Auth().PopAuthType()
var returningExprs vitess.SelectExprs
if returning, ok := node.Returning.(*tree.ReturningExprs); ok {
// TODO: PostgreSQL will apply any triggers before returning the value; need to test this.
returningExprs, err = nodeSelectExprs(ctx, tree.SelectExprs(*returning))
if err != nil {
return nil, err
}
}
var ignore string
var onDuplicate vitess.OnDup
if node.OnConflict != nil {
if isIgnore(node.OnConflict) {
ignore = vitess.IgnoreStr
} else if supportedOnConflictClause(node.OnConflict) {
// TODO: we are ignoring the column names, which are used to infer which index under conflict is to be checked
updateExprs, err := nodeUpdateExprs(ctx, node.OnConflict.Exprs)
if err != nil {
return nil, err
}
for _, updateExpr := range updateExprs {
onDuplicate = append(onDuplicate, updateExpr)
}
} else {
return nil, errors.Errorf("the ON CONFLICT clause provided is not yet supported")
}
}
var tableName vitess.TableName
switch node := node.Table.(type) {
case *tree.AliasedTableExpr:
return nil, errors.Errorf("aliased inserts are not yet supported")
case *tree.TableName:
var err error
tableName, err = nodeTableName(ctx, node)
if err != nil {
return nil, err
}
case *tree.TableRef:
return nil, errors.Errorf("table refs are not yet supported")
default:
return nil, errors.Errorf("unknown table name type in INSERT: `%T`", node)
}
var columns []vitess.ColIdent
if len(node.Columns) > 0 {
columns = make([]vitess.ColIdent, len(node.Columns))
for i := range node.Columns {
columns[i] = vitess.NewColIdent(string(node.Columns[i]))
}
}
with, err := nodeWith(ctx, node.With)
if err != nil {
return nil, err
}
var rows vitess.InsertRows
rows, err = nodeSelect(ctx, node.Rows)
if err != nil {
return nil, err
}
// For a ValuesStatement with simple rows, GMS expects AliasedValues
if vSelect, ok := rows.(*vitess.Select); ok && len(vSelect.From) == 1 {
if aliasedStmt, ok := vSelect.From[0].(*vitess.AliasedTableExpr); ok {
if valsStmt, ok := aliasedStmt.Expr.(*vitess.ValuesStatement); ok {
var vals vitess.Values
if len(valsStmt.Rows) == 0 {
vals = []vitess.ValTuple{{}}
} else {
vals = valsStmt.Rows
}
rows = &vitess.AliasedValues{
Values: vals,
}
}
}
}
return &vitess.Insert{
Action: vitess.InsertStr,
Ignore: ignore,
Table: tableName,
Returning: returningExprs,
With: with,
Columns: columns,
Rows: rows,
OnDup: onDuplicate,
Auth: vitess.AuthInformation{
AuthType: auth.AuthType_INSERT,
TargetType: auth.AuthTargetType_TableIdentifiers,
TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()},
},
}, nil
}
// isIgnore returns true if the ON CONFLICT clause provided is equivalent to INSERT IGNORE in GMS
func isIgnore(conflict *tree.OnConflict) bool {
return conflict.ArbiterPredicate == nil &&
conflict.Exprs == nil &&
conflict.Where == nil &&
conflict.DoNothing
}
// supportedOnConflictClause returns true if the ON CONFLICT clause given can be represented as
// an ON DUPLICATE KEY UPDATE clause in GMS
func supportedOnConflictClause(conflict *tree.OnConflict) bool {
if conflict.ArbiterPredicate != nil {
return false
}
if conflict.Where != nil {
return false
}
return true
}