chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeAliasedTableExpr handles *tree.AliasedTableExpr nodes.
|
||||
func nodeAliasedTableExpr(ctx *Context, node *tree.AliasedTableExpr) (*vitess.AliasedTableExpr, error) {
|
||||
if node.Ordinality {
|
||||
return nil, errors.Errorf("ordinality is not yet supported")
|
||||
}
|
||||
if node.IndexFlags != nil {
|
||||
return nil, errors.Errorf("index flags are not yet supported")
|
||||
}
|
||||
var aliasExpr vitess.SimpleTableExpr
|
||||
var authInfo vitess.AuthInformation
|
||||
|
||||
switch expr := node.Expr.(type) {
|
||||
case *tree.TableName:
|
||||
tableName, err := nodeTableName(ctx, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aliasExpr = tableName
|
||||
authInfo = vitess.AuthInformation{
|
||||
AuthType: ctx.Auth().PeekAuthType(),
|
||||
TargetType: auth.AuthTargetType_TableIdentifiers,
|
||||
TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()},
|
||||
}
|
||||
case *tree.Subquery:
|
||||
tableExpr, err := nodeTableExpr(ctx, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ate, ok := tableExpr.(*vitess.AliasedTableExpr)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("expected *vitess.AliasedTableExpr, found %T", tableExpr)
|
||||
}
|
||||
|
||||
var selectStmt vitess.SelectStatement
|
||||
switch ate.Expr.(type) {
|
||||
case *vitess.Subquery:
|
||||
selectStmt = ate.Expr.(*vitess.Subquery).Select
|
||||
default:
|
||||
return nil, errors.Errorf("unhandled subquery table expression: `%T`", tableExpr)
|
||||
}
|
||||
|
||||
// If the subquery is a VALUES statement, it should be represented more directly
|
||||
innerSelect := selectStmt
|
||||
if parentSelect, ok := innerSelect.(*vitess.ParenSelect); ok {
|
||||
innerSelect = parentSelect.Select
|
||||
}
|
||||
if inSelect, ok := innerSelect.(*vitess.Select); ok {
|
||||
if isTrivialSelectStar(inSelect) {
|
||||
if aliasedTblExpr, ok := inSelect.From[0].(*vitess.AliasedTableExpr); ok {
|
||||
if valuesStmt, ok := aliasedTblExpr.Expr.(*vitess.ValuesStatement); ok {
|
||||
if len(node.As.Cols) > 0 {
|
||||
columns := make([]vitess.ColIdent, len(node.As.Cols))
|
||||
for i := range node.As.Cols {
|
||||
columns[i] = vitess.NewColIdent(string(node.As.Cols[i]))
|
||||
}
|
||||
valuesStmt.Columns = columns
|
||||
}
|
||||
aliasExpr = valuesStmt
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subquery := &vitess.Subquery{
|
||||
Select: selectStmt,
|
||||
}
|
||||
|
||||
if len(node.As.Cols) > 0 {
|
||||
columns := make([]vitess.ColIdent, len(node.As.Cols))
|
||||
for i := range node.As.Cols {
|
||||
columns[i] = vitess.NewColIdent(string(node.As.Cols[i]))
|
||||
}
|
||||
subquery.Columns = columns
|
||||
}
|
||||
aliasExpr = subquery
|
||||
case *tree.RowsFromExpr:
|
||||
tableExpr, err := nodeTableExpr(ctx, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: this should be represented as a table function more directly
|
||||
subquery := &vitess.Subquery{
|
||||
Select: &vitess.Select{
|
||||
From: vitess.TableExprs{tableExpr},
|
||||
},
|
||||
}
|
||||
|
||||
if len(node.As.Cols) > 0 {
|
||||
columns := make([]vitess.ColIdent, len(node.As.Cols))
|
||||
for i := range node.As.Cols {
|
||||
columns[i] = vitess.NewColIdent(string(node.As.Cols[i]))
|
||||
}
|
||||
subquery.Columns = columns
|
||||
}
|
||||
aliasExpr = subquery
|
||||
default:
|
||||
return nil, errors.Errorf("unhandled table expression: `%T`", expr)
|
||||
}
|
||||
alias := string(node.As.Alias)
|
||||
|
||||
var asOf *vitess.AsOf
|
||||
if node.AsOf != nil {
|
||||
asOfExpr, err := nodeExpr(ctx, node.AsOf.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: other forms of AS OF (not just point in time)
|
||||
asOf = &vitess.AsOf{
|
||||
Time: asOfExpr,
|
||||
}
|
||||
}
|
||||
|
||||
return &vitess.AliasedTableExpr{
|
||||
Expr: aliasExpr,
|
||||
As: vitess.NewTableIdent(alias),
|
||||
AsOf: asOf,
|
||||
Lateral: node.Lateral,
|
||||
Auth: authInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isTrivialSelectStar returns true when the Select is just "SELECT * FROM <single table>"
|
||||
// with no other clauses that would alter semantics (no WHERE, ORDER BY, LIMIT, GROUP BY,
|
||||
// HAVING, DISTINCT, or WITH).
|
||||
func isTrivialSelectStar(s *vitess.Select) bool {
|
||||
if len(s.From) != 1 ||
|
||||
s.QueryOpts.Distinct ||
|
||||
s.With != nil ||
|
||||
s.Limit != nil ||
|
||||
len(s.OrderBy) != 0 ||
|
||||
s.Where != nil ||
|
||||
len(s.GroupBy) != 0 ||
|
||||
s.Having != nil ||
|
||||
len(s.SelectExprs) != 1 {
|
||||
return false
|
||||
}
|
||||
starExpr, ok := s.SelectExprs[0].(*vitess.StarExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return starExpr.TableName.IsEmpty()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterAggregate handles *tree.AlterAggregate nodes.
|
||||
func nodeAlterAggregate(ctx *Context, node *tree.AlterAggregate) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := validateAggArgMode(ctx, node.AggSig.Args, node.AggSig.OrderByArgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER AGGREGATE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterDatabase handles *tree.AlterDatabase nodes.
|
||||
func nodeAlterDatabase(ctx *Context, node *tree.AlterDatabase) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if node.Owner != "" && len(node.Options) == 0 {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER DATABASE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterDefaultPrivileges handles *tree.AlterDefaultPrivileges nodes.
|
||||
func nodeAlterDefaultPrivileges(ctx *Context, node *tree.AlterDefaultPrivileges) (vitess.Statement, error) {
|
||||
return NotYetSupportedError("ALTER DEFAULT PRIVILEGES statement is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterDomain handles ALTER DOMAIN nodes.
|
||||
func nodeAlterDomain(ctx *Context, stmt *tree.AlterDomain) (sqlparser.Statement, error) {
|
||||
if stmt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if _, ok := stmt.Cmd.(*tree.AlterDomainOwner); ok {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER DOMAIN is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterFunction handles *tree.AlterFunction nodes.
|
||||
func nodeAlterFunction(ctx *Context, node *tree.AlterFunction) (vitess.Statement, error) {
|
||||
_, err := validateRoutineOptions(ctx, node.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if node.Owner != "" && len(node.Options) == 0 {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER FUNCTION statement is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterIndex handles *tree.AlterIndex nodes.
|
||||
func nodeAlterIndex(ctx *Context, node *tree.AlterIndex) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Only PARTITION alterations are supported by the parser, so there's nothing to convert to yet
|
||||
return NotYetSupportedError("ALTER INDEX is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterMaterializedView handles *tree.AlterMaterializedView nodes.
|
||||
func nodeAlterMaterializedView(ctx *Context, node *tree.AlterMaterializedView) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER MATERIALIZED VIEW is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterProcedure handles *tree.AlterProcedure nodes.
|
||||
func nodeAlterProcedure(ctx *Context, node *tree.AlterProcedure) (vitess.Statement, error) {
|
||||
_, err := validateRoutineOptions(ctx, node.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if node.Owner != "" && len(node.Options) == 0 {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER PROCEDURE statement is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeAlterRole handles *tree.AlterRole nodes.
|
||||
func nodeAlterRole(ctx *Context, node *tree.AlterRole) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Name) == 0 {
|
||||
// The parser should make this impossible, but extra error checking is never bad
|
||||
return nil, errors.New(`role name cannot be empty`)
|
||||
}
|
||||
// Some of the keys are used as markers, and do not contain values.
|
||||
// Therefore, the values will be nil since they're ignored.
|
||||
options := make(map[string]any)
|
||||
for _, kvOption := range node.KVOptions {
|
||||
optionName := strings.ToUpper(string(kvOption.Key))
|
||||
switch optionName {
|
||||
case "BYPASSRLS":
|
||||
options["BYPASSRLS"] = nil
|
||||
case "CONNECTION_LIMIT":
|
||||
switch value := kvOption.Value.(type) {
|
||||
case *tree.DInt:
|
||||
if value == nil {
|
||||
options["CONNECTION_LIMIT"] = int32(-1)
|
||||
} else {
|
||||
// We enforce that only int32 values will fit here in the parser
|
||||
options["CONNECTION_LIMIT"] = int32(*value)
|
||||
}
|
||||
case tree.NullLiteral:
|
||||
options["CONNECTION_LIMIT"] = int32(-1)
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
case "CREATEDB":
|
||||
options["CREATEDB"] = nil
|
||||
case "CREATEROLE":
|
||||
options["CREATEROLE"] = nil
|
||||
case "INHERIT":
|
||||
options["INHERIT"] = nil
|
||||
case "LOGIN":
|
||||
options["LOGIN"] = nil
|
||||
case "NOBYPASSRLS":
|
||||
options["NOBYPASSRLS"] = nil
|
||||
case "NOCREATEDB":
|
||||
options["NOCREATEDB"] = nil
|
||||
case "NOCREATEROLE":
|
||||
options["NOCREATEROLE"] = nil
|
||||
case "NOINHERIT":
|
||||
options["NOINHERIT"] = nil
|
||||
case "NOLOGIN":
|
||||
options["NOLOGIN"] = nil
|
||||
case "NOREPLICATION":
|
||||
options["NOREPLICATION"] = nil
|
||||
case "NOSUPERUSER":
|
||||
options["NOSUPERUSER"] = nil
|
||||
case "PASSWORD":
|
||||
switch value := kvOption.Value.(type) {
|
||||
case *tree.DString:
|
||||
if value == nil {
|
||||
options["PASSWORD"] = nil
|
||||
} else {
|
||||
options["PASSWORD"] = (*string)(value)
|
||||
}
|
||||
case tree.NullLiteral:
|
||||
options["PASSWORD"] = nil
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
case "REPLICATION":
|
||||
options["REPLICATION"] = nil
|
||||
case "SUPERUSER":
|
||||
options["SUPERUSER"] = nil
|
||||
case "SYSID":
|
||||
// This is an option that is ignored by Postgres. Assuming it used to be relevant, but not any longer.
|
||||
case "VALID_UNTIL":
|
||||
strVal, ok := kvOption.Value.(*tree.DString)
|
||||
if !ok {
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
if strVal == nil {
|
||||
options["VALID_UNTIL"] = nil
|
||||
} else {
|
||||
options["VALID_UNTIL"] = (*string)(strVal)
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option "%s"`, kvOption.Key)
|
||||
}
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.AlterRole{
|
||||
Name: node.Name,
|
||||
Options: options,
|
||||
},
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterSchema handles *tree.AlterSchema nodes.
|
||||
func nodeAlterSchema(ctx *Context, node *tree.AlterSchema) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if _, ok := node.Cmd.(*tree.AlterSchemaOwner); ok {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER SCHEMA is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeAlterSequence handles *tree.AlterSequence nodes.
|
||||
func nodeAlterSequence(ctx *Context, node *tree.AlterSequence) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
if len(node.Owner) > 0 {
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if len(node.Options) == 0 {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
} else {
|
||||
warnings = append(warnings, "OWNER TO is unsupported and ignored")
|
||||
}
|
||||
}
|
||||
if node.SetLog {
|
||||
return NotYetSupportedError("LOGGED and UNLOGGED are not yet supported")
|
||||
}
|
||||
|
||||
nodeName := node.Name.ToTableName()
|
||||
name, err := nodeTableName(ctx, &nodeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(name.DbQualifier.String()) > 0 {
|
||||
return NotYetSupportedError("ALTER SEQUENCE does not yet support specifying the database")
|
||||
}
|
||||
|
||||
ownedBy := pgnodes.AlterSequenceOwnedBy{}
|
||||
for _, option := range node.Options {
|
||||
switch option.Name {
|
||||
case tree.SeqOptOwnedBy:
|
||||
ownedBy.IsSet = true
|
||||
// OWNED BY NONE is valid, so we have to check if a column was provided
|
||||
if option.ColumnItemVal != nil {
|
||||
expr, err := nodeExpr(ctx, option.ColumnItemVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
colName, ok := expr.(*vitess.ColName)
|
||||
if !ok {
|
||||
return nil, errors.New("expected sequence owner to be a table and column name")
|
||||
}
|
||||
if colName.Qualifier.SchemaQualifier.String() != name.SchemaQualifier.String() {
|
||||
return nil, errors.New("ALTER SEQUENCE must use the same schema for the sequence and owned table")
|
||||
}
|
||||
if len(colName.Qualifier.DbQualifier.String()) > 0 {
|
||||
return nil, errors.New("database specification is not yet supported for sequences")
|
||||
}
|
||||
if len(colName.Name.String()) == 0 || len(colName.Qualifier.Name.String()) == 0 {
|
||||
return nil, errors.New("invalid OWNED BY option")
|
||||
}
|
||||
ownedBy.Table = colName.Qualifier.Name.String()
|
||||
ownedBy.Column = colName.Name.String()
|
||||
}
|
||||
default:
|
||||
return NotYetSupportedError(fmt.Sprintf("%s is not yet supported", option.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewAlterSequence(
|
||||
node.IfExists,
|
||||
name.SchemaQualifier.String(),
|
||||
name.Name.String(),
|
||||
ownedBy,
|
||||
warnings...),
|
||||
Children: nil,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_UPDATE,
|
||||
TargetType: auth.AuthTargetType_SequenceIdentifiers,
|
||||
TargetNames: []string{name.SchemaQualifier.String(), name.Name.String()},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,494 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeAlterTable handles *tree.AlterTable nodes.
|
||||
func nodeAlterTable(ctx *Context, node *tree.AlterTable) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
treeTableName := node.Table.ToTableName()
|
||||
tableName, err := nodeTableName(ctx, &treeTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't implement sequence addition/modification as a DDL since it's not in GMS, so we only support one of these
|
||||
// at a time. If there are more than one, then we'll return an error in the command loop.
|
||||
if len(node.Cmds) == 1 {
|
||||
cmd, ok := node.Cmds[0].(*tree.AlterTableComputed)
|
||||
if ok {
|
||||
return nodeAlterTableComputed(ctx, treeTableName, cmd)
|
||||
}
|
||||
if vcmd, ok := node.Cmds[0].(*tree.AlterTableValidateConstraint); ok {
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewValidateConstraint(
|
||||
tableName.SchemaQualifier.String(),
|
||||
tableName.Name.String(),
|
||||
bareIdentifier(vcmd.Constraint),
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
statements, noOps, err := nodeAlterTableCmds(ctx, node.Cmds, tableName, node.IfExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there are no valid statements return a no-op statement
|
||||
if len(noOps) > 0 && len(statements) == 0 {
|
||||
return NewNoOp(noOps...), nil
|
||||
}
|
||||
|
||||
// Otherwise emit warnings now, then return an AlterTable statement
|
||||
// TODO: we don't have a way to send or store the warnings alongside a valid AlterTable statement. We could either
|
||||
// get a *sql.Context here and emit warnings, or we could store the warnings in the Context and make the caller
|
||||
// emit them before it sends |ReadyForQuery|
|
||||
// if len(noOps) > 0 {
|
||||
// emit warnings
|
||||
// }
|
||||
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
Statements: statements,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetSchema handles *tree.AlterTableSetSchema nodes.
|
||||
func nodeAlterTableSetSchema(ctx *Context, node *tree.AlterTableSetSchema) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Errorf("ALTER TABLE SET SCHEMA is not yet supported")
|
||||
}
|
||||
|
||||
// nodeAlterTableCmds converts tree.AlterTableCmds into a slice of vitess.DDL instances that can be executed by GMS.
|
||||
// |tableName| identifies the table being altered, and |ifExists| indicates whether the IF EXISTS clause was specified.
|
||||
// A second slice of unsupported but safely ignored statements is return as well.
|
||||
func nodeAlterTableCmds(
|
||||
ctx *Context,
|
||||
node tree.AlterTableCmds,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) ([]*vitess.DDL, []string, error) {
|
||||
|
||||
if len(node) == 0 {
|
||||
return nil, nil, errors.Errorf("no commands specified for ALTER TABLE statement")
|
||||
}
|
||||
|
||||
vitessDdlCmds := make([]*vitess.DDL, 0, len(node))
|
||||
var unsupportedWarnings []string
|
||||
for _, cmd := range node {
|
||||
var err error
|
||||
var statement *vitess.DDL
|
||||
switch cmd := cmd.(type) {
|
||||
case *tree.AlterTableAddConstraint:
|
||||
statement, err = nodeAlterTableAddConstraint(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableAddColumn:
|
||||
statement, err = nodeAlterTableAddColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
|
||||
// If inline constraints have been specified, set the ConstraintAction so that they get processed
|
||||
if len(statement.TableSpec.Constraints) > 0 {
|
||||
statement.ConstraintAction = vitess.AddStr
|
||||
}
|
||||
|
||||
case *tree.AlterTableDropColumn:
|
||||
statement, err = nodeAlterTableDropColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableDropConstraint:
|
||||
statement, err = nodeAlterTableDropConstraint(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableRenameColumn:
|
||||
statement, err = nodeAlterTableRenameColumn(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableSetDefault:
|
||||
statement, err = nodeAlterTableSetDefault(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableDropNotNull:
|
||||
statement, err = nodeAlterTableDropNotNull(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableSetNotNull:
|
||||
statement, err = nodeAlterTableSetNotNull(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableAlterColumnType:
|
||||
statement, err = nodeAlterTableAlterColumnType(ctx, cmd, tableName, ifExists)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
vitessDdlCmds = append(vitessDdlCmds, statement)
|
||||
case *tree.AlterTableOwner:
|
||||
unsupportedWarnings = append(unsupportedWarnings, fmt.Sprintf("ALTER TABLE %s OWNER TO %s", tableName.String(), cmd.Owner))
|
||||
case *tree.AlterTableComputed:
|
||||
return nil, nil, errors.New("This command does not currently support multiple actions in one statement")
|
||||
case *tree.AlterTableSetStatistics:
|
||||
// is unsupported and ignored
|
||||
case *tree.AlterTableRowLevelSecurity:
|
||||
// is unsupported and ignored
|
||||
case *tree.AlterTableReplicaIdentity:
|
||||
// is unsupported and ignored
|
||||
default:
|
||||
return nil, nil, errors.Errorf("ALTER TABLE with unsupported command type %T", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
return vitessDdlCmds, unsupportedWarnings, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableAddConstraint converts a tree.AlterTableAddConstraint instance
|
||||
// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies
|
||||
// the table being altered, and |ifExists| indicates whether the IF EXISTS clause
|
||||
// was specified.
|
||||
func nodeAlterTableAddConstraint(
|
||||
ctx *Context,
|
||||
node *tree.AlterTableAddConstraint,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) (*vitess.DDL, error) {
|
||||
|
||||
notValid := node.ValidationBehavior == tree.ValidationSkip
|
||||
if notValid {
|
||||
if _, ok := node.ConstraintDef.(*tree.UniqueConstraintTableDef); ok {
|
||||
return nil, errors.Errorf("NOT VALID is not applicable to UNIQUE or PRIMARY KEY constraints")
|
||||
}
|
||||
}
|
||||
|
||||
switch constraintDef := node.ConstraintDef.(type) {
|
||||
case *tree.CheckConstraintTableDef:
|
||||
return nodeCheckConstraintTableDef(ctx, constraintDef, tableName, ifExists, notValid)
|
||||
case *tree.UniqueConstraintTableDef:
|
||||
return nodeUniqueConstraintTableDef(ctx, constraintDef, tableName, ifExists)
|
||||
case *tree.ForeignKeyConstraintTableDef:
|
||||
foreignKeyDefinition, err := nodeForeignKeyConstraintTableDef(ctx, constraintDef, notValid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ConstraintAction: "add",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
TableSpec: &vitess.TableSpec{
|
||||
Constraints: []*vitess.ConstraintDefinition{
|
||||
{
|
||||
Name: bareIdentifier(constraintDef.Name),
|
||||
Details: foreignKeyDefinition,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported constraint "+
|
||||
"definition type %T", node)
|
||||
}
|
||||
}
|
||||
|
||||
// bareIdentifier returns the string representation of a name without any quoting
|
||||
// (quoted is the default Name.String() behavior)
|
||||
func bareIdentifier(id tree.Name) string {
|
||||
ctx := tree.NewFmtCtx(tree.FmtBareIdentifiers)
|
||||
id.Format(ctx)
|
||||
return ctx.CloseAndGetString()
|
||||
}
|
||||
|
||||
// nodeAlterTableAddColumn converts a tree.AlterTableAddColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableAddColumn(ctx *Context, node *tree.AlterTableAddColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.IfNotExists {
|
||||
return nil, errors.Errorf("IF NOT EXISTS on a column in an ADD COLUMN statement is not supported yet")
|
||||
}
|
||||
|
||||
vitessColumnDef, err := nodeColumnTableDef(ctx, node.ColumnDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableSpec := &vitess.TableSpec{}
|
||||
tableSpec.AddColumn(vitessColumnDef)
|
||||
|
||||
if node.ColumnDef.References.Table != nil {
|
||||
constraintDef, err := nodeForeignKeyDefinitionFromColumnTableDef(ctx, node.ColumnDef.Name, node.ColumnDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tableSpec.AddConstraint(&vitess.ConstraintDefinition{
|
||||
Name: string(node.ColumnDef.References.ConstraintName),
|
||||
Details: constraintDef,
|
||||
})
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "add",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitessColumnDef.Name,
|
||||
TableSpec: tableSpec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableDropColumn converts a tree.AlterTableDropColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableDropColumn(ctx *Context, node *tree.AlterTableDropColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.IfExists {
|
||||
return nil, errors.Errorf("IF EXISTS on a column in a DROP COLUMN statement is not supported yet")
|
||||
}
|
||||
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("ALTER TABLE DROP COLUMN does not support RESTRICT option")
|
||||
case tree.DropCascade:
|
||||
logrus.Warnf("CASCADE option on DROP COLUMN is not yet supported, ignoring")
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported drop behavior %v", node.DropBehavior)
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "drop",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableRenameColumn converts a tree.AlterTableRenameColumn instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableRenameColumn(ctx *Context, node *tree.AlterTableRenameColumn, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
ColumnAction: "rename",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
ToColumn: vitess.NewColIdent(node.NewName.String()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetDefault converts a tree.AlterTableSetDefault instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableSetDefault(ctx *Context, node *tree.AlterTableSetDefault, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
expr, err := nodeExpr(ctx, node.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GMS requires the AST to wrap function expressions in parens
|
||||
if _, ok := expr.(*vitess.FuncExpr); ok {
|
||||
expr = &vitess.ParenExpr{Expr: expr}
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
DefaultSpec: &vitess.DefaultSpec{
|
||||
Action: "set",
|
||||
Column: vitess.NewColIdent(bareIdentifier(node.Column)),
|
||||
Value: expr,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableAlterColumnType converts a tree.AlterTableAlterColumnType instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableAlterColumnType(ctx *Context, node *tree.AlterTableAlterColumnType, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
if node.Collation != "" {
|
||||
return nil, errors.Errorf("ALTER TABLE with COLLATE is not supported yet")
|
||||
}
|
||||
|
||||
if node.Using != nil {
|
||||
return nil, errors.Errorf("ALTER TABLE with USING is not supported yet")
|
||||
}
|
||||
|
||||
convertType, resolvedType, err := nodeResolvableTypeReference(ctx, node.ToType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resolvedType == pgtypes.Record {
|
||||
return nil, errors.Errorf(`column "%s" has pseudo-type record`, node.Column.String())
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
ColumnTypeSpec: &vitess.ColumnTypeSpec{
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
Type: vitess.ColumnType{
|
||||
Type: convertType.Type,
|
||||
ResolvedType: resolvedType,
|
||||
Length: convertType.Length,
|
||||
Scale: convertType.Scale,
|
||||
Charset: convertType.Charset,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableDropNotNull converts a tree.AlterTableDropNotNull instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableDropNotNull(ctx *Context, node *tree.AlterTableDropNotNull, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
NotNullSpec: &vitess.NotNullSpec{
|
||||
Action: "drop",
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetNotNull converts a tree.AlterTableSetNotNull instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTableSetNotNull(ctx *Context, node *tree.AlterTableSetNotNull, tableName vitess.TableName, ifExists bool) (*vitess.DDL, error) {
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
NotNullSpec: &vitess.NotNullSpec{
|
||||
Action: "set",
|
||||
Column: vitess.NewColIdent(node.Column.String()),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableSetNotNull converts a tree.AlterTablePartition instance into an equivalent vitess.DDL instance.
|
||||
func nodeAlterTablePartition(ctx *Context, node *tree.AlterTablePartition) (*vitess.AlterTable, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: This is an incomplete translation because our GMS implementation doesn't support the MySQL
|
||||
// equivalent of these statements either. Regardless, these are all no-ops.
|
||||
treeTableName := node.Name.ToTableName()
|
||||
tableName, err := nodeTableName(ctx, &treeTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch node.Spec.Type {
|
||||
case tree.PartitionBoundIn:
|
||||
case tree.PartitionBoundFromTo:
|
||||
case tree.PartitionBoundWith:
|
||||
default:
|
||||
return nil, errors.Errorf("ALTER TABLE with unsupported partition type %v", node.Spec.Type)
|
||||
}
|
||||
|
||||
partitionDef := &vitess.PartitionDefinition{
|
||||
Name: vitess.NewColIdent(node.Partition.String()),
|
||||
}
|
||||
|
||||
actionStr := ""
|
||||
if node.IsDetach {
|
||||
actionStr = vitess.DropStr
|
||||
} else {
|
||||
actionStr = vitess.AddStr
|
||||
}
|
||||
|
||||
partitionSpec := &vitess.PartitionSpec{
|
||||
Action: actionStr,
|
||||
Definitions: []*vitess.PartitionDefinition{partitionDef},
|
||||
}
|
||||
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
PartitionSpecs: []*vitess.PartitionSpec{partitionSpec},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableComputed converts a tree.AlterTableComputed instance into an equivalent CREATE/ALTER SEQUENCE statement.
|
||||
func nodeAlterTableComputed(ctx *Context, tableName tree.TableName, node *tree.AlterTableComputed) (vitess.Statement, error) {
|
||||
if len(node.Defs) > 0 {
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
var persistence tree.Persistence
|
||||
var seqName tree.TableName
|
||||
var trimmedOptions []tree.SequenceOption
|
||||
switch def := node.AddDefs.(type) {
|
||||
case *tree.ColumnComputedDef:
|
||||
if def.Expr != nil {
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
trimmedOptions = make([]tree.SequenceOption, 0, len(def.Options)+2)
|
||||
for _, opt := range def.Options {
|
||||
switch opt.Name {
|
||||
case tree.SeqOptOwnedBy:
|
||||
return NotYetSupportedError("OWNED BY is invalid here")
|
||||
case tree.SeqOptRestart:
|
||||
return NotYetSupportedError("RESTART is not yet supported")
|
||||
case tree.SeqOptName:
|
||||
seqName = opt.SeqName
|
||||
case tree.SeqOptLogged:
|
||||
persistence = tree.PersistencePermanent
|
||||
case tree.SeqOptUnlogged:
|
||||
persistence = tree.PersistenceUnlogged
|
||||
default:
|
||||
trimmedOptions = append(trimmedOptions, opt)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return NotYetSupportedError("ALTER TABLE variant is not yet supported")
|
||||
}
|
||||
trimmedOptions = append(trimmedOptions,
|
||||
tree.SequenceOption{
|
||||
Name: tree.SeqOptOwnedBy,
|
||||
ColumnItemVal: tree.NewColumnItem(&tableName, node.Column),
|
||||
},
|
||||
tree.SequenceOption{
|
||||
Name: tree.SeqOptViaAlterTable,
|
||||
},
|
||||
)
|
||||
return nodeCreateSequence(ctx, &tree.CreateSequence{
|
||||
IfNotExists: false,
|
||||
Name: seqName,
|
||||
Persistence: persistence,
|
||||
Options: trimmedOptions,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterType handles *tree.AlterType nodes.
|
||||
func nodeAlterType(ctx *Context, node *tree.AlterType) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if _, ok := node.Cmd.(*tree.AlterTypeOwner); ok {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER TYPE is not yet supported")
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2025 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/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeAlterView handles ALTER VIEW nodes.
|
||||
func nodeAlterView(ctx *Context, stmt *tree.AlterView) (sqlparser.Statement, error) {
|
||||
if stmt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// We intentionally don't support OWNER TO since we don't support owning objects
|
||||
if _, ok := stmt.Cmd.(*tree.AlterViewOwnerTo); ok {
|
||||
return NewNoOp("OWNER TO is unsupported and ignored"), nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER VIEW is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeAnalyze handles *tree.Analyze nodes.
|
||||
func nodeAnalyze(ctx *Context, node *tree.Analyze) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If no tables were specified, return an empty Analyze statement, and the analyzer
|
||||
// will populate all tables to be analyzed.
|
||||
if node.Table == nil {
|
||||
return &vitess.Analyze{}, nil
|
||||
}
|
||||
|
||||
objectName, ok := node.Table.(*tree.UnresolvedObjectName)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unsupported table type in Analyze node: %T", node.Table)
|
||||
}
|
||||
|
||||
return &vitess.Analyze{Tables: []vitess.TableName{
|
||||
{
|
||||
Name: vitess.NewTableIdent(objectName.Object()),
|
||||
SchemaQualifier: vitess.NewTableIdent(objectName.Schema()),
|
||||
DbQualifier: vitess.NewTableIdent(objectName.Catalog()),
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeBackup handles *tree.Backup nodes.
|
||||
func nodeBackup(ctx *Context, node *tree.Backup) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("BACKUP is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeBeginTransaction handles *tree.BeginTransaction nodes.
|
||||
func nodeBeginTransaction(ctx *Context, node *tree.BeginTransaction) (*vitess.Begin, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Modes.Isolation != tree.UnspecifiedIsolation {
|
||||
return nil, errors.Errorf("isolation levels are not yet supported")
|
||||
}
|
||||
if node.Modes.UserPriority != tree.UnspecifiedUserPriority {
|
||||
return nil, errors.Errorf("user priority is not yet supported")
|
||||
}
|
||||
if node.Modes.AsOf.Expr != nil {
|
||||
return nil, errors.Errorf("AS OF is not yet supported")
|
||||
}
|
||||
if node.Modes.Deferrable != tree.UnspecifiedDeferrableMode {
|
||||
return nil, errors.Errorf("deferrability is not yet supported")
|
||||
}
|
||||
var characteristic string
|
||||
switch node.Modes.ReadWriteMode {
|
||||
case tree.UnspecifiedReadWriteMode:
|
||||
characteristic = vitess.TxReadWrite
|
||||
case tree.ReadOnly:
|
||||
characteristic = vitess.TxReadOnly
|
||||
case tree.ReadWrite:
|
||||
characteristic = vitess.TxReadWrite
|
||||
default:
|
||||
return nil, errors.Errorf("unknown READ/WRITE setting")
|
||||
}
|
||||
return &vitess.Begin{
|
||||
TransactionCharacteristic: characteristic,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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/server/auth"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeCall handles *tree.Call nodes.
|
||||
func nodeCall(ctx *Context, node *tree.Call) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Procedure.Type != 0 {
|
||||
return nil, errors.Errorf("procedure distinction is not yet supported")
|
||||
}
|
||||
if node.Procedure.Filter != nil {
|
||||
return nil, errors.Errorf("procedure filters are not yet supported")
|
||||
}
|
||||
if node.Procedure.WindowDef != nil {
|
||||
return nil, errors.Errorf("procedure window definitions are not yet supported")
|
||||
}
|
||||
if node.Procedure.AggType == tree.OrderedSetAgg {
|
||||
return nil, errors.Errorf("procedure aggregation is not yet supported")
|
||||
}
|
||||
if len(node.Procedure.OrderBy) > 0 {
|
||||
return nil, errors.Errorf("procedure ORDER BY is not yet supported")
|
||||
}
|
||||
|
||||
ctx.Auth().PushAuthType(auth.AuthType_EXECUTE)
|
||||
defer ctx.Auth().PopAuthType()
|
||||
|
||||
var qualifier vitess.TableIdent
|
||||
var name vitess.ColIdent
|
||||
switch funcRef := node.Procedure.Func.FunctionReference.(type) {
|
||||
case *tree.FunctionDefinition:
|
||||
name = vitess.NewColIdent(funcRef.Name)
|
||||
case *tree.UnresolvedName:
|
||||
if funcRef.NumParts > 2 {
|
||||
return nil, errors.Errorf("referencing items outside the schema or database is not yet supported")
|
||||
}
|
||||
if funcRef.NumParts == 2 {
|
||||
qualifier = vitess.NewTableIdent(funcRef.Parts[1])
|
||||
}
|
||||
name = vitess.NewColIdent(funcRef.Parts[0])
|
||||
default:
|
||||
return nil, errors.Errorf("unknown function reference")
|
||||
}
|
||||
exprs, err := nodeExprs(ctx, node.Procedure.Exprs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCall(qualifier.String(), name.String(), exprs),
|
||||
Children: exprs,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_EXECUTE,
|
||||
TargetType: auth.AuthTargetType_FunctionIdentifiers,
|
||||
TargetNames: []string{qualifier.String(), name.String()},
|
||||
// TODO: need to get argument types separated by comma ( check routineArgTypesKey function )
|
||||
Extra: "",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCancelQueries handles *tree.CancelQueries nodes.
|
||||
func nodeCancelQueries(ctx *Context, node *tree.CancelQueries) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CANCEL QUERIES is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCancelSessions handles *tree.CancelSessions nodes.
|
||||
func nodeCancelSessions(ctx *Context, node *tree.CancelSessions) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CANCEL SESSIONS is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCannedOptPlan handles *tree.CannedOptPlan nodes.
|
||||
func nodeCannedOptPlan(ctx *Context, node *tree.CannedOptPlan) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("PREPARE AS OPT PLAN is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// DoltCreateTablePlaceholderSequenceName is a Placeholder name used in translating computed columns to generated
|
||||
// columns that involve a sequence, used later in analysis
|
||||
const DoltCreateTablePlaceholderSequenceName = "dolt_create_table_placeholder_sequence"
|
||||
|
||||
// nodeColumnTableDef handles *tree.ColumnTableDef nodes.
|
||||
func nodeColumnTableDef(ctx *Context, node *tree.ColumnTableDef) (*vitess.ColumnDefinition, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Nullable.ConstraintName) > 0 ||
|
||||
len(node.DefaultExpr.ConstraintName) > 0 ||
|
||||
len(node.UniqueConstraintName) > 0 {
|
||||
return nil, errors.Errorf("non-foreign key column constraint names are not yet supported")
|
||||
}
|
||||
convertType, resolvedType, err := nodeResolvableTypeReference(ctx, node.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resolvedType == pgtypes.Record {
|
||||
return nil, errors.Errorf(`column "%s" has pseudo-type record`, node.Name.String())
|
||||
}
|
||||
|
||||
var isNull vitess.BoolVal
|
||||
var isNotNull vitess.BoolVal
|
||||
switch node.Nullable.Nullability {
|
||||
case tree.NotNull:
|
||||
isNull = false
|
||||
isNotNull = true
|
||||
case tree.Null:
|
||||
isNull = true
|
||||
isNotNull = false
|
||||
case tree.SilentNull:
|
||||
isNull = true
|
||||
isNotNull = false
|
||||
default:
|
||||
return nil, errors.Errorf("unknown NULL type encountered")
|
||||
}
|
||||
keyOpt := vitess.ColumnKeyOption(0) // colKeyNone, unexported for some reason
|
||||
if node.PrimaryKey.IsPrimaryKey {
|
||||
keyOpt = 1 // colKeyPrimary
|
||||
isNull = false
|
||||
isNotNull = true
|
||||
} else if node.Unique {
|
||||
keyOpt = 3 // colKeyUnique
|
||||
}
|
||||
defaultExpr, err := nodeExpr(ctx, node.DefaultExpr.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Wrap any default expression using a function call in parens to match MySQL's column default requirements
|
||||
if _, ok := defaultExpr.(*vitess.FuncExpr); ok {
|
||||
defaultExpr = &vitess.ParenExpr{Expr: defaultExpr}
|
||||
}
|
||||
|
||||
if node.References.Table != nil {
|
||||
if len(node.References.Col) == 0 {
|
||||
return nil, errors.Errorf("implicit primary key matching on column foreign key is not yet supported")
|
||||
}
|
||||
// Callers register the foreign key via the table constraint list, so ForeignKeyDef
|
||||
// is left unset here to avoid creating the same constraint twice.
|
||||
}
|
||||
|
||||
if len(node.Computed.Options) > 0 {
|
||||
return nil, errors.Errorf("sequence options are not yet supported, create a sequence separately")
|
||||
}
|
||||
|
||||
var generated vitess.Expr
|
||||
hasGeneratedExpr := node.IsComputed() && node.Computed.Expr != nil
|
||||
computedByDefaultAsIdentity := node.IsComputed() && !hasGeneratedExpr && node.Computed.ByDefault
|
||||
computedAsIdentity := node.IsComputed() && !hasGeneratedExpr && !node.Computed.ByDefault
|
||||
|
||||
if hasGeneratedExpr {
|
||||
generated, err = nodeExpr(ctx, node.Computed.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if computedAsIdentity {
|
||||
generated, err = nodeExpr(ctx, &tree.FuncExpr{
|
||||
Func: tree.WrapFunction("nextval"),
|
||||
Exprs: tree.Exprs{
|
||||
tree.NewStrVal(DoltCreateTablePlaceholderSequenceName),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if generated != nil {
|
||||
// GMS requires the AST to wrap function expressions in parens
|
||||
if _, ok := generated.(*vitess.FuncExpr); ok {
|
||||
generated = &vitess.ParenExpr{Expr: generated}
|
||||
}
|
||||
|
||||
// clean up the expressions generated here. our default expression handling generates aliases that aren't
|
||||
// appropriate in this context.
|
||||
generated = clearAliases(generated)
|
||||
}
|
||||
|
||||
if node.IsSerial || computedByDefaultAsIdentity || computedAsIdentity {
|
||||
if resolvedType.IsEmptyType() {
|
||||
return nil, errors.Errorf("serial type was not resolvable")
|
||||
}
|
||||
switch resolvedType.ID {
|
||||
case pgtypes.Int16.ID:
|
||||
resolvedType = pgtypes.Int16Serial
|
||||
case pgtypes.Int32.ID:
|
||||
resolvedType = pgtypes.Int32Serial
|
||||
case pgtypes.Int64.ID:
|
||||
resolvedType = pgtypes.Int64Serial
|
||||
default:
|
||||
return nil, errors.Errorf(`type "%s" cannot be serial`, resolvedType.String())
|
||||
}
|
||||
if defaultExpr != nil {
|
||||
return nil, errors.Errorf(`multiple default values specified for column "%s"`, node.Name)
|
||||
}
|
||||
}
|
||||
|
||||
colDef := &vitess.ColumnDefinition{
|
||||
Name: vitess.NewColIdent(string(node.Name)),
|
||||
Type: vitess.ColumnType{
|
||||
Type: convertType.Type,
|
||||
ResolvedType: resolvedType,
|
||||
Null: isNull,
|
||||
NotNull: isNotNull,
|
||||
Autoincrement: false,
|
||||
Default: defaultExpr,
|
||||
Length: convertType.Length,
|
||||
Scale: convertType.Scale,
|
||||
KeyOpt: keyOpt,
|
||||
GeneratedExpr: generated,
|
||||
Stored: generated != nil, // postgres generated columns are always stored, never virtual
|
||||
},
|
||||
}
|
||||
|
||||
if len(node.CheckExprs) > 0 {
|
||||
// TODO: vitess does not support multiple check constraint on a single column
|
||||
if len(node.CheckExprs) > 1 {
|
||||
return nil, errors.Errorf("column-declared multiple CHECK expressions are not yet supported")
|
||||
}
|
||||
var checkConstraints = make([]*vitess.ConstraintDefinition, len(node.CheckExprs))
|
||||
for i, checkExpr := range node.CheckExprs {
|
||||
expr, err := nodeExpr(ctx, checkExpr.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
checkConstraints[i] = &vitess.ConstraintDefinition{
|
||||
Name: string(checkExpr.ConstraintName),
|
||||
Details: &vitess.CheckConstraintDefinition{
|
||||
Expr: expr,
|
||||
Enforced: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
// until we support multiple constraints in a column
|
||||
colDef.Type.Constraint = checkConstraints[0]
|
||||
}
|
||||
return colDef, nil
|
||||
}
|
||||
|
||||
// clearAliases removes As and InputExpression from any AliasedExpr in the expression tree given. This is required
|
||||
// in some contexts where we expect the expression to serialize to a string without any alias names.
|
||||
func clearAliases(e vitess.Expr) vitess.Expr {
|
||||
_ = vitess.Walk(func(node vitess.SQLNode) (kontinue bool, err error) {
|
||||
if expr, ok := node.(*vitess.AliasedExpr); ok {
|
||||
expr.As = vitess.ColIdent{}
|
||||
expr.InputExpression = ""
|
||||
}
|
||||
return true, nil
|
||||
}, e)
|
||||
return e
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeComment handles *tree.Comment nodes.
|
||||
func nodeComment(ctx *Context, node *tree.Comment) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NewNoOp("COMMENT ON is not yet supported"), nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCommitTransaction handles *tree.CommitTransaction nodes.
|
||||
func nodeCommitTransaction(ctx *Context, node *tree.CommitTransaction) (*vitess.Commit, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &vitess.Commit{}, nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCheckConstraintTableDef converts a tree.CheckConstraintTableDef instance
|
||||
// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies
|
||||
// the table being altered, and |ifExists| indicates whether the IF EXISTS clause
|
||||
// was specified. |notValid| reflects whether the NOT VALID clause was present.
|
||||
func nodeCheckConstraintTableDef(
|
||||
ctx *Context,
|
||||
node *tree.CheckConstraintTableDef,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool,
|
||||
notValid bool) (*vitess.DDL, error) {
|
||||
|
||||
if node.NoInherit {
|
||||
return nil, errors.Errorf("NO INHERIT is not yet supported for check constraints")
|
||||
}
|
||||
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
ConstraintAction: "add",
|
||||
TableSpec: &vitess.TableSpec{
|
||||
Constraints: []*vitess.ConstraintDefinition{
|
||||
{
|
||||
Name: node.Name.String(),
|
||||
Details: &vitess.CheckConstraintDefinition{
|
||||
Expr: expr,
|
||||
Enforced: true,
|
||||
NotValid: notValid,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeAlterTableDropConstraint converts a tree.AlterTableDropConstraint instance
|
||||
// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies
|
||||
// the table being altered, and |ifExists| indicates whether the IF EXISTS clause
|
||||
// was specified.
|
||||
func nodeAlterTableDropConstraint(
|
||||
ctx *Context,
|
||||
node *tree.AlterTableDropConstraint,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) (*vitess.DDL, error) {
|
||||
|
||||
if node.DropBehavior == tree.DropCascade {
|
||||
return nil, errors.Errorf("CASCADE is not yet supported for drop constraint")
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
ConstraintAction: "drop",
|
||||
ConstraintIfExists: node.IfExists,
|
||||
TableSpec: &vitess.TableSpec{
|
||||
Constraints: []*vitess.ConstraintDefinition{
|
||||
{Name: node.Constraint.String()},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nodeUniqueConstraintTableDef converts a tree.UniqueConstraintTableDef instance
|
||||
// into a vitess.DDL instance that can be executed by GMS. |tableName| identifies
|
||||
// the table being altered, and |ifExists| indicates whether the IF EXISTS clause
|
||||
// was specified.
|
||||
func nodeUniqueConstraintTableDef(
|
||||
ctx *Context,
|
||||
node *tree.UniqueConstraintTableDef,
|
||||
tableName vitess.TableName,
|
||||
ifExists bool) (*vitess.DDL, error) {
|
||||
|
||||
if len(node.IndexParams.StorageParams) > 0 {
|
||||
return nil, errors.Errorf("STORAGE parameters not yet supported for indexes")
|
||||
}
|
||||
|
||||
if node.IndexParams.Tablespace != "" {
|
||||
return nil, errors.Errorf("TABLESPACE is not yet supported")
|
||||
}
|
||||
|
||||
if node.NullsNotDistinct {
|
||||
return nil, errors.Errorf("NULLS NOT DISTINCT is not yet supported")
|
||||
}
|
||||
|
||||
indexFields, err := nodeIndexElemList(ctx, node.Columns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexType := "unique"
|
||||
if node.PrimaryKey {
|
||||
indexType = "primary"
|
||||
}
|
||||
|
||||
return &vitess.DDL{
|
||||
Action: "alter",
|
||||
Table: tableName,
|
||||
IfExists: ifExists,
|
||||
IndexSpec: &vitess.IndexSpec{
|
||||
ToName: vitess.NewColIdent(bareIdentifier(node.Name)),
|
||||
Action: "create",
|
||||
Type: indexType,
|
||||
Fields: indexFields,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/dolthub/doltgresql/postgres/parser/parser"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
)
|
||||
|
||||
// Context contains any relevant context for the AST conversion. For example, the auth system uses the context to
|
||||
// determine which larger statement an expression exists in, which may influence how the expression should handle
|
||||
// authorization.
|
||||
type Context struct {
|
||||
authContext *auth.AuthContext
|
||||
originalQuery string
|
||||
}
|
||||
|
||||
// NewContext returns a new *Context.
|
||||
func NewContext(postgresStmt parser.Statement) *Context {
|
||||
return &Context{
|
||||
authContext: auth.NewAuthContext(),
|
||||
originalQuery: postgresStmt.SQL,
|
||||
}
|
||||
}
|
||||
|
||||
// Auth returns the portion that handles authentication.
|
||||
func (ctx *Context) Auth() *auth.AuthContext {
|
||||
return ctx.authContext
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeControlJobs handles *tree.ControlJobs nodes.
|
||||
func nodeControlJobs(ctx *Context, node *tree.ControlJobs) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("PAUSE/RESUME/CANCEL are not yet supported")
|
||||
}
|
||||
|
||||
// nodeControlJobsForSchedules handles *tree.ControlJobsForSchedules nodes.
|
||||
func nodeControlJobsForSchedules(ctx *Context, node *tree.ControlJobsForSchedules) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("PAUSE/RESUME/CANCEL are not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeControlSchedules handles *tree.ControlSchedules nodes.
|
||||
func nodeControlSchedules(ctx *Context, node *tree.ControlSchedules) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("PAUSE/RESUME SCHEDULE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// 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/parser"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// UnknownColSentinelPrefix is prepended to the index position of unaliased string literals in a
|
||||
// SELECT clause. This gives GMS a unique per-position alias so its alias scope map can distinguish
|
||||
// multiple unnamed columns in the same SELECT. schemaToFieldDescriptions in doltgres_handler.go
|
||||
// strips this prefix and returns Postgres's "?column?" placeholder on the wire.
|
||||
const UnknownColSentinelPrefix = "__?column?__"
|
||||
|
||||
// Convert converts a Postgres AST into a Vitess AST.
|
||||
func Convert(postgresStmt parser.Statement) (vitess.Statement, error) {
|
||||
ctx := NewContext(postgresStmt)
|
||||
switch stmt := postgresStmt.AST.(type) {
|
||||
case *tree.AlterAggregate:
|
||||
return nodeAlterAggregate(ctx, stmt)
|
||||
case *tree.AlterDatabase:
|
||||
return nodeAlterDatabase(ctx, stmt)
|
||||
case *tree.AlterDefaultPrivileges:
|
||||
return nodeAlterDefaultPrivileges(ctx, stmt)
|
||||
case *tree.AlterDomain:
|
||||
return nodeAlterDomain(ctx, stmt)
|
||||
case *tree.AlterFunction:
|
||||
return nodeAlterFunction(ctx, stmt)
|
||||
case *tree.AlterIndex:
|
||||
return nodeAlterIndex(ctx, stmt)
|
||||
case *tree.AlterMaterializedView:
|
||||
return nodeAlterMaterializedView(ctx, stmt)
|
||||
case *tree.AlterProcedure:
|
||||
return nodeAlterProcedure(ctx, stmt)
|
||||
case *tree.AlterRole:
|
||||
return nodeAlterRole(ctx, stmt)
|
||||
case *tree.AlterSchema:
|
||||
return nodeAlterSchema(ctx, stmt)
|
||||
case *tree.AlterSequence:
|
||||
return nodeAlterSequence(ctx, stmt)
|
||||
case *tree.AlterTable:
|
||||
return nodeAlterTable(ctx, stmt)
|
||||
case *tree.AlterTablePartition:
|
||||
return nodeAlterTablePartition(ctx, stmt)
|
||||
case *tree.AlterTableSetSchema:
|
||||
return nodeAlterTableSetSchema(ctx, stmt)
|
||||
case *tree.AlterType:
|
||||
return nodeAlterType(ctx, stmt)
|
||||
case *tree.AlterView:
|
||||
return nodeAlterView(ctx, stmt)
|
||||
case *tree.Analyze:
|
||||
return nodeAnalyze(ctx, stmt)
|
||||
case *tree.Backup:
|
||||
return nodeBackup(ctx, stmt)
|
||||
case *tree.BeginTransaction:
|
||||
return nodeBeginTransaction(ctx, stmt)
|
||||
case *tree.Call:
|
||||
return nodeCall(ctx, stmt)
|
||||
case *tree.CancelQueries:
|
||||
return nodeCancelQueries(ctx, stmt)
|
||||
case *tree.CancelSessions:
|
||||
return nodeCancelSessions(ctx, stmt)
|
||||
case *tree.CannedOptPlan:
|
||||
return nodeCannedOptPlan(ctx, stmt)
|
||||
case *tree.Comment:
|
||||
return nodeComment(ctx, stmt)
|
||||
case *tree.CommitTransaction:
|
||||
return nodeCommitTransaction(ctx, stmt)
|
||||
case *tree.ControlJobs:
|
||||
return nodeControlJobs(ctx, stmt)
|
||||
case *tree.ControlJobsForSchedules:
|
||||
return nodeControlJobsForSchedules(ctx, stmt)
|
||||
case *tree.ControlSchedules:
|
||||
return nodeControlSchedules(ctx, stmt)
|
||||
case *tree.CopyFrom:
|
||||
return nodeCopyFrom(ctx, stmt)
|
||||
case *tree.CreateAggregate:
|
||||
return nodeCreateAggregate(ctx, stmt)
|
||||
case *tree.CreateCast:
|
||||
return nodeCreateCast(ctx, stmt)
|
||||
case *tree.CreateChangefeed:
|
||||
return nodeCreateChangefeed(ctx, stmt)
|
||||
case *tree.CreateDatabase:
|
||||
return nodeCreateDatabase(ctx, stmt)
|
||||
case *tree.CreateDomain:
|
||||
return nodeCreateDomain(ctx, stmt)
|
||||
case *tree.CreateExtension:
|
||||
return nodeCreateExtension(ctx, stmt)
|
||||
case *tree.CreateFunction:
|
||||
return nodeCreateFunction(ctx, stmt)
|
||||
case *tree.CreateIndex:
|
||||
return nodeCreateIndex(ctx, stmt)
|
||||
case *tree.CreateMaterializedView:
|
||||
return nodeCreateMaterializedView(ctx, stmt)
|
||||
case *tree.CreateProcedure:
|
||||
return nodeCreateProcedure(ctx, stmt)
|
||||
case *tree.CreateRole:
|
||||
return nodeCreateRole(ctx, stmt)
|
||||
case *tree.CreateSchema:
|
||||
return nodeCreateSchema(ctx, stmt)
|
||||
case *tree.CreateSequence:
|
||||
return nodeCreateSequence(ctx, stmt)
|
||||
case *tree.CreateStats:
|
||||
return nodeCreateStats(ctx, stmt)
|
||||
case *tree.CreateTable:
|
||||
return nodeCreateTable(ctx, stmt)
|
||||
case *tree.CreateTrigger:
|
||||
return nodeCreateTrigger(ctx, stmt)
|
||||
case *tree.CreateType:
|
||||
return nodeCreateType(ctx, stmt)
|
||||
case *tree.CreateView:
|
||||
return nodeCreateView(ctx, stmt)
|
||||
case *tree.Deallocate:
|
||||
return nodeDeallocate(ctx, stmt)
|
||||
case *tree.Delete:
|
||||
return nodeDelete(ctx, stmt)
|
||||
case *tree.Discard:
|
||||
return nodeDiscard(ctx, stmt)
|
||||
case *tree.DropAggregate:
|
||||
return nodeDropAggregate(ctx, stmt)
|
||||
case *tree.DropCast:
|
||||
return nodeDropCast(ctx, stmt)
|
||||
case *tree.DropDatabase:
|
||||
return nodeDropDatabase(ctx, stmt)
|
||||
case *tree.DropDomain:
|
||||
return nodeDropDomain(ctx, stmt)
|
||||
case *tree.DropExtension:
|
||||
return nodeDropExtension(ctx, stmt)
|
||||
case *tree.DropFunction:
|
||||
return nodeDropFunction(ctx, stmt)
|
||||
case *tree.DropIndex:
|
||||
return nodeDropIndex(ctx, stmt)
|
||||
case *tree.DropProcedure:
|
||||
return nodeDropProcedure(ctx, stmt)
|
||||
case *tree.DropRole:
|
||||
return nodeDropRole(ctx, stmt)
|
||||
case *tree.DropSchema:
|
||||
return nodeDropSchema(ctx, stmt)
|
||||
case *tree.DropSequence:
|
||||
return nodeDropSequence(ctx, stmt)
|
||||
case *tree.DropTable:
|
||||
return nodeDropTable(ctx, stmt)
|
||||
case *tree.DropTrigger:
|
||||
return nodeDropTrigger(ctx, stmt)
|
||||
case *tree.DropType:
|
||||
return nodeDropType(ctx, stmt)
|
||||
case *tree.DropView:
|
||||
return nodeDropView(ctx, stmt)
|
||||
case *tree.Execute:
|
||||
return nodeExecute(ctx, stmt)
|
||||
case *tree.Explain:
|
||||
return nodeExplain(ctx, stmt)
|
||||
case *tree.ExplainAnalyzeDebug:
|
||||
return nodeExplainAnalyzeDebug(ctx, stmt)
|
||||
case *tree.Export:
|
||||
return nodeExport(ctx, stmt)
|
||||
case *tree.Grant:
|
||||
return nodeGrant(ctx, stmt)
|
||||
case *tree.GrantRole:
|
||||
return nodeGrantRole(ctx, stmt)
|
||||
case *tree.Import:
|
||||
return nodeImport(ctx, stmt)
|
||||
case *tree.Insert:
|
||||
return nodeInsert(ctx, stmt)
|
||||
case *tree.ParenSelect:
|
||||
return nodeParenSelect(ctx, stmt)
|
||||
case *tree.Prepare:
|
||||
return nodePrepare(ctx, stmt)
|
||||
case *tree.RefreshMaterializedView:
|
||||
return nodeRefreshMaterializedView(ctx, stmt)
|
||||
case *tree.ReleaseSavepoint:
|
||||
return nodeReleaseSavepoint(ctx, stmt)
|
||||
case *tree.Relocate:
|
||||
return nodeRelocate(ctx, stmt)
|
||||
case *tree.RenameColumn:
|
||||
return nodeRenameColumn(ctx, stmt)
|
||||
case *tree.RenameDatabase:
|
||||
return nodeRenameDatabase(ctx, stmt)
|
||||
case *tree.RenameIndex:
|
||||
return nodeRenameIndex(ctx, stmt)
|
||||
case *tree.RenameTable:
|
||||
return nodeRenameTable(ctx, stmt)
|
||||
case *tree.ReparentDatabase:
|
||||
return nodeReparentDatabase(ctx, stmt)
|
||||
case *tree.Restore:
|
||||
return nodeRestore(ctx, stmt)
|
||||
case *tree.Return:
|
||||
return nodeReturn(ctx, stmt)
|
||||
case *tree.Revoke:
|
||||
return nodeRevoke(ctx, stmt)
|
||||
case *tree.RevokeRole:
|
||||
return nodeRevokeRole(ctx, stmt)
|
||||
case *tree.RollbackToSavepoint:
|
||||
return nodeRollbackToSavepoint(ctx, stmt)
|
||||
case *tree.RollbackTransaction:
|
||||
return nodeRollbackTransaction(ctx, stmt)
|
||||
case *tree.Savepoint:
|
||||
return nodeSavepoint(ctx, stmt)
|
||||
case *tree.Scatter:
|
||||
return nodeScatter(ctx, stmt)
|
||||
case *tree.ScheduledBackup:
|
||||
return nodeScheduledBackup(ctx, stmt)
|
||||
case *tree.Scrub:
|
||||
return nodeScrub(ctx, stmt)
|
||||
case *tree.Select:
|
||||
return nodeSelect(ctx, stmt)
|
||||
case *tree.SelectClause:
|
||||
return nodeSelectClause(ctx, stmt)
|
||||
case *tree.SetSessionAuthorization:
|
||||
return nodeSetSessionAuthorization(ctx, stmt)
|
||||
case *tree.SetSessionCharacteristics:
|
||||
return nodeSetSessionCharacteristics(ctx, stmt)
|
||||
case *tree.SetTransaction:
|
||||
return nodeSetTransaction(ctx, stmt)
|
||||
case *tree.SetVar:
|
||||
return nodeSetVar(ctx, stmt)
|
||||
case *tree.ShowBackup:
|
||||
return nodeShowBackup(ctx, stmt)
|
||||
case *tree.ShowColumns:
|
||||
return nodeShowColumns(ctx, stmt)
|
||||
case *tree.ShowConstraints:
|
||||
return nodeShowConstraints(ctx, stmt)
|
||||
case *tree.ShowCreate:
|
||||
return nodeShowCreate(ctx, stmt)
|
||||
case *tree.ShowDatabaseIndexes:
|
||||
return nodeShowDatabaseIndexes(ctx, stmt)
|
||||
case *tree.ShowDatabases:
|
||||
return nodeShowDatabases(ctx, stmt)
|
||||
case *tree.ShowEnums:
|
||||
return nodeShowEnums(ctx, stmt)
|
||||
case *tree.ShowFingerprints:
|
||||
return nodeShowFingerprints(ctx, stmt)
|
||||
case *tree.ShowGrants:
|
||||
return nodeShowGrants(ctx, stmt)
|
||||
case *tree.ShowHistogram:
|
||||
return nodeShowHistogram(ctx, stmt)
|
||||
case *tree.ShowIndexes:
|
||||
return nodeShowIndexes(ctx, stmt)
|
||||
case *tree.ShowJobs:
|
||||
return nodeShowJobs(ctx, stmt)
|
||||
case *tree.ShowLastQueryStatistics:
|
||||
return nodeShowLastQueryStatistics(ctx, stmt)
|
||||
case *tree.ShowPartitions:
|
||||
return nodeShowPartitions(ctx, stmt)
|
||||
case *tree.ShowQueries:
|
||||
return nodeShowQueries(ctx, stmt)
|
||||
case *tree.ShowRoleGrants:
|
||||
return nodeShowRoleGrants(ctx, stmt)
|
||||
case *tree.ShowRoles:
|
||||
return nodeShowRoles(ctx, stmt)
|
||||
case *tree.ShowSavepointStatus:
|
||||
return nodeShowSavepointStatus(ctx, stmt)
|
||||
case *tree.ShowSchedules:
|
||||
return nodeShowSchedules(ctx, stmt)
|
||||
case *tree.ShowSchemas:
|
||||
return nodeShowSchemas(ctx, stmt)
|
||||
case *tree.ShowSequences:
|
||||
return nodeShowSequences(ctx, stmt)
|
||||
case *tree.ShowSessions:
|
||||
return nodeShowSessions(ctx, stmt)
|
||||
case *tree.ShowSyntax:
|
||||
return nodeShowSyntax(ctx, stmt)
|
||||
case *tree.ShowTableStats:
|
||||
return nodeShowTableStats(ctx, stmt)
|
||||
case *tree.ShowTables:
|
||||
return nodeShowTables(ctx, stmt)
|
||||
case *tree.ShowTraceForSession:
|
||||
return nodeShowTraceForSession(ctx, stmt)
|
||||
case *tree.ShowTransactionStatus:
|
||||
return nodeShowTransactionStatus(ctx, stmt)
|
||||
case *tree.ShowTransactions:
|
||||
return nodeShowTransactions(ctx, stmt)
|
||||
case *tree.ShowTypes:
|
||||
return nodeShowTypes(ctx, stmt)
|
||||
case *tree.ShowUsers:
|
||||
return nodeShowUsers(ctx, stmt)
|
||||
case *tree.ShowVar:
|
||||
return nodeShowVar(ctx, stmt)
|
||||
case *tree.Split:
|
||||
return nodeSplit(ctx, stmt)
|
||||
case *tree.Truncate:
|
||||
return nodeTruncate(ctx, stmt)
|
||||
case *tree.UnionClause:
|
||||
return nodeUnionClause(ctx, stmt)
|
||||
case *tree.Unsplit:
|
||||
return nodeUnsplit(ctx, stmt)
|
||||
case *tree.Update:
|
||||
return nodeUpdate(ctx, stmt)
|
||||
case *tree.Vacuum:
|
||||
return nodeVacuum(ctx, stmt)
|
||||
case *tree.ValuesClause:
|
||||
return nodeValuesClause(ctx, stmt)
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown statement type encountered: `%T`", stmt)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeCopyFrom handles *tree.CopyFrom nodes.
|
||||
func nodeCopyFrom(ctx *Context, node *tree.CopyFrom) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Options.CopyFormat == tree.CopyFormatBinary {
|
||||
return nil, errors.Errorf("COPY FROM does not support format BINARY")
|
||||
}
|
||||
|
||||
// We start by creating a stub insert statement for the COPY FROM statement, which we will use to build a basic
|
||||
// INSERT plan for. At runtime we will swap out the bogus row values for our actual data read from STDIN.
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
tableName, err := nodeTableName(ctx, &node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stubValues := make(vitess.Values, 1)
|
||||
stubValues[0] = make(vitess.ValTuple, len(columns))
|
||||
for i := range columns {
|
||||
stubValues[0][i] = &vitess.NullVal{}
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCopyFrom(
|
||||
node.Table.Catalog(),
|
||||
doltdb.TableName{
|
||||
Name: node.Table.Object(),
|
||||
Schema: node.Table.Schema(),
|
||||
},
|
||||
node.Options,
|
||||
node.File,
|
||||
node.Stdin,
|
||||
node.Columns,
|
||||
&vitess.Insert{
|
||||
Action: vitess.InsertStr,
|
||||
Table: tableName,
|
||||
Columns: columns,
|
||||
Rows: &vitess.AliasedValues{
|
||||
Values: stubValues,
|
||||
},
|
||||
},
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateAggregate handles *tree.CreateAggregate nodes.
|
||||
func nodeCreateAggregate(ctx *Context, node *tree.CreateAggregate) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !ignoreUnsupportedStatements {
|
||||
if err := validateAggArgMode(ctx, node.Args, node.OrderByArgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CREATE AGGREGATE is not yet supported")
|
||||
}
|
||||
|
||||
// validateAggArgMode checks routine arguments for `OUT` and `INOUT` modes,
|
||||
// which cannot be used for AGGREGATE arguments.
|
||||
func validateAggArgMode(ctx *Context, args, orderByArgs tree.RoutineArgs) error {
|
||||
for _, sig := range args {
|
||||
if sig.Mode == tree.RoutineArgModeOut || sig.Mode == tree.RoutineArgModeInout {
|
||||
return errors.Errorf("aggregate functions do not support OUT or INOUT arguments")
|
||||
}
|
||||
}
|
||||
for _, sig := range orderByArgs {
|
||||
if sig.Mode == tree.RoutineArgModeOut || sig.Mode == tree.RoutineArgModeInout {
|
||||
return errors.Errorf("aggregate functions do not support OUT or INOUT arguments")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/casts"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeCreateCast handles *tree.CreateCast nodes.
|
||||
func nodeCreateCast(ctx *Context, node *tree.CreateCast) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
_, sourceType, err := nodeResolvableTypeReference(ctx, node.Source, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, targetType, err := nodeResolvableTypeReference(ctx, node.Target, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var funcName tree.TableName
|
||||
var funcParams []pgnodes.RoutineParam
|
||||
switch node.Type {
|
||||
case tree.CreateCastType_WithFunction:
|
||||
funcName = node.FuncName.ToTableName()
|
||||
funcParams = make([]pgnodes.RoutineParam, len(node.FuncArgs))
|
||||
for i, arg := range node.FuncArgs {
|
||||
funcParams[i].Name = arg.Name.String()
|
||||
_, funcParams[i].Type, err = nodeResolvableTypeReference(ctx, arg.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case tree.CreateCastType_WithoutFunction:
|
||||
// TODO: restrict to superusers only with error: "must be superuser to create a cast WITHOUT FUNCTION"
|
||||
case tree.CreateCastType_Inout:
|
||||
// Nothing to do
|
||||
default:
|
||||
panic("unhandled case")
|
||||
}
|
||||
var scope casts.CastType
|
||||
switch node.Scope {
|
||||
case tree.CreateCastScope_Explicit:
|
||||
scope = casts.CastType_Explicit
|
||||
case tree.CreateCastScope_Assignment:
|
||||
scope = casts.CastType_Assignment
|
||||
case tree.CreateCastScope_Implicit:
|
||||
scope = casts.CastType_Implicit
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateCast(
|
||||
sourceType,
|
||||
targetType,
|
||||
scope,
|
||||
node.Type,
|
||||
funcName.Schema(),
|
||||
funcName.Table(),
|
||||
funcParams,
|
||||
),
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_TODO,
|
||||
TargetNames: []string{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateChangefeed handles *tree.CreateChangefeed nodes.
|
||||
func nodeCreateChangefeed(ctx *Context, node *tree.CreateChangefeed) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CREATE CHANGEFEED is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateDatabase handles *tree.CreateDatabase nodes.
|
||||
func nodeCreateDatabase(_ *Context, node *tree.CreateDatabase) (*vitess.DBDDL, error) {
|
||||
var charsets []*vitess.CharsetAndCollate
|
||||
|
||||
if len(node.Template) > 0 {
|
||||
// TODO: special casing "template0", as some tests make use of it and we need them to pass for now
|
||||
if node.Template != "template0" {
|
||||
return nil, errors.Errorf("TEMPLATE clause is not yet supported")
|
||||
}
|
||||
}
|
||||
if len(node.Encoding) > 0 {
|
||||
logrus.Warnf("unsupported clause ENCODING, ignoring")
|
||||
}
|
||||
if len(node.Strategy) > 0 {
|
||||
return nil, errors.Errorf("STRATEGY clause is not yet supported")
|
||||
}
|
||||
if len(node.Locale) > 0 {
|
||||
logrus.Warnf("unsupported clause LC_LOCALE, ignoring")
|
||||
}
|
||||
if len(node.Collate) > 0 {
|
||||
collation, charset, err := parseLocaleString(node.Collate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if collation == "" {
|
||||
logrus.Warnf("unsupported LC_COLLATE, ignoring")
|
||||
} else {
|
||||
charsets = append(charsets,
|
||||
&vitess.CharsetAndCollate{
|
||||
Type: "CHARACTER SET",
|
||||
Value: charset,
|
||||
},
|
||||
&vitess.CharsetAndCollate{
|
||||
Type: "COLLATE",
|
||||
Value: collation,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if len(node.CType) > 0 {
|
||||
logrus.Warnf("CTYPE clause is not yet supported, ignoring")
|
||||
}
|
||||
if len(node.IcuLocale) > 0 {
|
||||
return nil, errors.Errorf("ICU_LOCALE clause is not yet supported")
|
||||
}
|
||||
if len(node.IcuRules) > 0 {
|
||||
return nil, errors.Errorf("TEMPLATE clause is not yet supported")
|
||||
}
|
||||
if len(node.LocaleProvider) > 0 {
|
||||
return nil, errors.Errorf("LOCALE_PROVIDER clause is not yet supported")
|
||||
}
|
||||
if len(node.CollationVersion) > 0 {
|
||||
return nil, errors.Errorf("COLLATION_VERSION clause is not yet supported")
|
||||
}
|
||||
if len(node.Tablespace) > 0 {
|
||||
return nil, errors.Errorf("TABLESPACE clause is not yet supported")
|
||||
}
|
||||
// TODO: some clauses have default values in case of not being defined.
|
||||
// ALLOW_CONNECTIONS defaults to TRUE
|
||||
if node.AllowConnections != nil {
|
||||
return nil, errors.Errorf("ALLOW_CONNECTIONS clause is not yet supported")
|
||||
}
|
||||
// CONNECTION LIMIT defaults to -1
|
||||
if node.ConnectionLimit != nil {
|
||||
return nil, errors.Errorf("CONNECTION LIMIT clause is not yet supported")
|
||||
}
|
||||
// IS_TEMPLATE defaults to FALSE
|
||||
if node.IsTemplate != nil {
|
||||
return nil, errors.Errorf("IS_TEMPLATE clause is not yet supported")
|
||||
}
|
||||
if node.Oid != nil {
|
||||
return nil, errors.Errorf("OID clause is not yet supported")
|
||||
}
|
||||
|
||||
return &vitess.DBDDL{
|
||||
Action: vitess.CreateStr,
|
||||
SchemaOrDatabase: "database",
|
||||
DBName: bareIdentifier(node.Name),
|
||||
IfNotExists: node.IfNotExists,
|
||||
CharsetCollate: charsets,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var collationRegex = regexp.MustCompile(`^(?P<Language>[^_]+)_?(?P<Region>[^.]+)?\.?(?P<CodePage>\d+)?$`)
|
||||
|
||||
// parseLocaleString attempts to parse the locale string given to extract a mysql collation we can use
|
||||
func parseLocaleString(collation string) (string, string, error) {
|
||||
// FindStringSubmatchIndex returns the indices of the matched elements
|
||||
match := collationRegex.FindStringSubmatch(collation)
|
||||
|
||||
result := make(map[string]string)
|
||||
for i, name := range collationRegex.SubexpNames() {
|
||||
if i > 0 && i <= len(match) {
|
||||
result[name] = match[i]
|
||||
}
|
||||
}
|
||||
|
||||
if result["Language"] == "" {
|
||||
return "", "", errors.Errorf("malformed collation: %s", collation)
|
||||
}
|
||||
|
||||
switch strings.ToLower(result["Language"]) {
|
||||
case "english", "en":
|
||||
return "latin1_general_cs", "latin1", nil
|
||||
}
|
||||
|
||||
return "", "", nil
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeCreateDomain handles *tree.CreateDomain nodes.
|
||||
func nodeCreateDomain(ctx *Context, node *tree.CreateDomain) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
name, err := nodeUnresolvedObjectName(ctx, node.TypeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, dataType, err := nodeResolvableTypeReference(ctx, node.DataType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dataType == pgtypes.Record {
|
||||
return nil, errors.Errorf(`"record" is not a valid base type for a domain`)
|
||||
}
|
||||
|
||||
if node.Collate != "" {
|
||||
return nil, errors.Errorf("domain collation is not yet supported")
|
||||
}
|
||||
var children []vitess.Expr
|
||||
if node.Default != nil {
|
||||
defExpr, err := nodeExpr(ctx, node.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Wrap any default expression using a function call in parens to match MySQL's column default requirements
|
||||
if _, ok := defExpr.(*vitess.FuncExpr); ok {
|
||||
defExpr = &vitess.ParenExpr{Expr: defExpr}
|
||||
}
|
||||
children = append(children, defExpr)
|
||||
}
|
||||
|
||||
var definedNotNull, definedNull bool
|
||||
var checkConstraintNames []string
|
||||
var checkConstraintExprs []vitess.Expr
|
||||
for _, constraint := range node.Constraints {
|
||||
if constraint.Check != nil {
|
||||
check, err := verifyAndReplaceValue(node.DataType, constraint.Check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expr, err := nodeExpr(ctx, check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checkConstraintNames = append(checkConstraintNames, string(constraint.Constraint))
|
||||
checkConstraintExprs = append(checkConstraintExprs, expr)
|
||||
} else if constraint.NotNull {
|
||||
definedNotNull = true
|
||||
if definedNull {
|
||||
return nil, errors.Errorf("conflicting NULL/NOT NULL constraints")
|
||||
}
|
||||
} else {
|
||||
definedNull = true
|
||||
if definedNotNull {
|
||||
return nil, errors.Errorf("conflicting NULL/NOT NULL constraints")
|
||||
}
|
||||
}
|
||||
}
|
||||
children = append(children, checkConstraintExprs...)
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.CreateDomain{
|
||||
SchemaName: name.SchemaQualifier.String(),
|
||||
Name: name.Name.String(),
|
||||
AsType: dataType,
|
||||
Collation: node.Collate,
|
||||
HasDefault: node.Default != nil,
|
||||
IsNotNull: definedNotNull,
|
||||
CheckConstraintNames: checkConstraintNames,
|
||||
},
|
||||
Children: children,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// verifyAndReplaceValue verifies that only VALUE is referenced and replaces it with DomainColumn.
|
||||
// This function should be used for DOMAIN statements only.
|
||||
func verifyAndReplaceValue(typ tree.ResolvableTypeReference, expr tree.Expr) (tree.Expr, error) {
|
||||
return tree.SimpleVisit(expr, func(visitingExpr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
|
||||
switch v := visitingExpr.(type) {
|
||||
case *tree.UnresolvedName:
|
||||
if strings.ToLower(v.String()) != "value" {
|
||||
return false, nil, errors.Errorf(`column "%s" does not exist`, v.String())
|
||||
}
|
||||
return false, tree.DomainColumn{Typ: typ}, nil
|
||||
}
|
||||
return true, visitingExpr, nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateExtension handles *tree.CreateExtension nodes.
|
||||
func nodeCreateExtension(ctx *Context, node *tree.CreateExtension) (vitess.Statement, error) {
|
||||
if len(node.Schema) > 0 {
|
||||
if node.Schema == "pg_catalog" && node.Name == "plpgsql" {
|
||||
return nil, nil
|
||||
} else if node.Schema != "public" {
|
||||
return NotYetSupportedError("non public SCHEMA is not yet supported")
|
||||
}
|
||||
// TODO filter out extensions we support
|
||||
}
|
||||
if len(node.Version) > 0 {
|
||||
return NotYetSupportedError("VERSION is not yet supported")
|
||||
}
|
||||
if node.Cascade {
|
||||
return NotYetSupportedError("CASCADE is not yet supported")
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateExtension(
|
||||
string(node.Name),
|
||||
node.IfNotExists,
|
||||
node.Schema,
|
||||
node.Version,
|
||||
node.Cascade,
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/parser"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeCreateFunction handles *tree.CreateFunction nodes.
|
||||
func nodeCreateFunction(ctx *Context, node *tree.CreateFunction) (vitess.Statement, error) {
|
||||
options, err := validateRoutineOptions(ctx, node.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Grab the general information that we'll need to create the function
|
||||
tableName := node.Name.ToTableName()
|
||||
var retType *pgtypes.DoltgresType
|
||||
if len(node.RetType) == 0 {
|
||||
retType = pgtypes.Void
|
||||
} else if !node.ReturnsTable {
|
||||
// Return types may specify "trigger", but this doesn't apply elsewhere
|
||||
_, retType, err = nodeResolvableTypeReference(ctx, node.RetType[0].Type, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
retType = createAnonymousCompositeType(node.RetType)
|
||||
}
|
||||
|
||||
params := make([]pgnodes.RoutineParam, len(node.Args))
|
||||
var defaults []vitess.Expr
|
||||
for i, arg := range node.Args {
|
||||
// parameter name
|
||||
params[i].Name = arg.Name.String()
|
||||
// parameter type
|
||||
_, params[i].Type, err = nodeResolvableTypeReference(ctx, arg.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// parameter default
|
||||
if arg.Default != nil {
|
||||
params[i].HasDefault = true
|
||||
d, err := nodeExpr(ctx, arg.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaults = append(defaults, d)
|
||||
}
|
||||
}
|
||||
var strict bool
|
||||
if nullInputOption, ok := options[tree.OptionNullInput]; ok {
|
||||
if nullInputOption.NullInput == tree.ReturnsNullOnNullInput || nullInputOption.NullInput == tree.StrictNullInput {
|
||||
strict = true
|
||||
}
|
||||
}
|
||||
// We only support PL/pgSQL, SQL and C for now, so we verify that here
|
||||
var parsedBody []plpgsql.InterpreterOperation
|
||||
var sqlDef string
|
||||
var sqlDefParsedStmts []vitess.Statement
|
||||
var extensionName, extensionSymbol string
|
||||
if languageOption, ok := options[tree.OptionLanguage]; ok {
|
||||
switch strings.ToLower(languageOption.Language) {
|
||||
case "plpgsql":
|
||||
// PL/pgSQL is different from standard Postgres SQL, so we have to use a special parser to handle it.
|
||||
// This parser also requires the full `CREATE FUNCTION` string, so we'll pass that.
|
||||
parsedBody, err = plpgsql.Parse(ctx.originalQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// parse types
|
||||
for i, op := range parsedBody {
|
||||
switch op.OpCode {
|
||||
case plpgsql.OpCode_Declare:
|
||||
// ParseType uses casting to parse the given type, but
|
||||
// some special types cannot be cast. Eg: `user_defined_table_type%ROWTYPE`
|
||||
if declareTyp, err := parser.ParseType(op.PrimaryData); err == nil {
|
||||
if _, dt, err := nodeResolvableTypeReference(ctx, declareTyp, false); err == nil && dt != nil {
|
||||
dtName := dt.Name()
|
||||
if dt.Schema() != "" {
|
||||
dtName = fmt.Sprintf("%s.%s", dt.Schema(), dtName)
|
||||
}
|
||||
parsedBody[i].PrimaryData = dtName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "sql":
|
||||
as, ok := options[tree.OptionAs1]
|
||||
if ok {
|
||||
sqlDef, sqlDefParsedStmts, err = handleLanguageSQLAs(as.Definition, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
sqlBody, ok := options[tree.OptionSqlBody]
|
||||
if ok {
|
||||
beginAtomic, ok := sqlBody.SqlBody.(*tree.BeginEndBlock)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Expected BEGIN in CREATE FUNCTION definition, got %T", sqlBody.SqlBody)
|
||||
}
|
||||
stmts := make([]parser.Statement, len(beginAtomic.Statements))
|
||||
for i, s := range beginAtomic.Statements {
|
||||
stmts[i] = parser.Statement{
|
||||
AST: s,
|
||||
SQL: s.String(),
|
||||
}
|
||||
}
|
||||
sqlDef, sqlDefParsedStmts, err = convertSQLStmts(stmts, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil, errors.Errorf("CREATE FUNCTION definition needed for LANGUAGE SQL")
|
||||
case "c":
|
||||
symbolOption, ok := options[tree.OptionAs2]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("LANGUAGE C is only supported when providing both the module name and symbol")
|
||||
}
|
||||
extensionName = symbolOption.ObjFile
|
||||
extensionSymbol = symbolOption.LinkSymbol
|
||||
default:
|
||||
return nil, errors.Errorf("CREATE FUNCTION only supports PL/pgSQL, C and SQL for now; others are not yet supported")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Errorf("CREATE FUNCTION does not define an input language")
|
||||
}
|
||||
// Returns the stored procedure call with all options
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateFunction(
|
||||
tableName.Table(),
|
||||
tableName.Schema(),
|
||||
node.Replace,
|
||||
retType,
|
||||
params,
|
||||
strict,
|
||||
ctx.originalQuery,
|
||||
extensionName,
|
||||
extensionSymbol,
|
||||
parsedBody,
|
||||
sqlDef,
|
||||
sqlDefParsedStmts,
|
||||
node.ReturnsSetOf || node.ReturnsTable,
|
||||
),
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_SchemaIdentifiers,
|
||||
TargetNames: []string{tableName.Catalog(), tableName.Schema()},
|
||||
},
|
||||
Children: defaults,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createAnonymousCompositeType creates a new DoltgresType for the anonymous composite return
|
||||
// type for a function, as represented by the |fieldTypes| that were specified in the function
|
||||
// definition.
|
||||
func createAnonymousCompositeType(fieldTypes []tree.SimpleColumnDef) *pgtypes.DoltgresType {
|
||||
attrs := make([]pgtypes.CompositeAttribute, len(fieldTypes))
|
||||
for i, fieldType := range fieldTypes {
|
||||
attrs[i] = pgtypes.NewCompositeAttribute(nil, id.Null, fieldType.Name.String(),
|
||||
pgtypes.NewUnresolvedDoltgresTypeFromID(id.NewType("", fieldType.Type.SQLString())), int16(i), "")
|
||||
}
|
||||
|
||||
typeIdString := "table("
|
||||
for i, attr := range attrs {
|
||||
if i > 0 {
|
||||
typeIdString += ","
|
||||
}
|
||||
typeIdString += attr.Name
|
||||
typeIdString += ":"
|
||||
typeIdString += attr.Type.ID.TypeName()
|
||||
}
|
||||
typeIdString += ")"
|
||||
|
||||
// NOTE: there is no schema needed, since these types are anonymous and can't be directly referenced
|
||||
typeId := id.NewType("", typeIdString)
|
||||
|
||||
return pgtypes.NewCompositeType(context.Background(), id.Null, nil, typeId, attrs)
|
||||
}
|
||||
|
||||
// handleLanguageSQLAs handles parsing SQL definition strings in both CREATE FUNCTION and CREATE PROCEDURE
|
||||
// and returns converted the sql statements into vitess statements.
|
||||
func handleLanguageSQLAs(definition string, params []pgnodes.RoutineParam) (string, []vitess.Statement, error) {
|
||||
stmts, err := parser.Parse(definition)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return convertSQLStmts(stmts, params)
|
||||
}
|
||||
|
||||
// convertSQLStmts takes parser.Statements and routine parameters and
|
||||
// returns converted to string representation and vitess statements.
|
||||
func convertSQLStmts(stmts parser.Statements, params []pgnodes.RoutineParam) (string, []vitess.Statement, error) {
|
||||
paramMap := make(map[string]*framework.ParamTypAndValue, len(params))
|
||||
for i, param := range params {
|
||||
tv := &framework.ParamTypAndValue{
|
||||
Typ: param.Type,
|
||||
Val: nil,
|
||||
FromCreate: true,
|
||||
}
|
||||
// placeholder name is empty
|
||||
if param.Name == "\"\"" {
|
||||
n := fmt.Sprintf("$%d", i+1)
|
||||
paramMap[n] = tv
|
||||
params[i].Name = n
|
||||
} else {
|
||||
paramMap[param.Name] = tv
|
||||
}
|
||||
}
|
||||
|
||||
var sqlDefs = make([]string, len(stmts))
|
||||
var vitessASTs = make([]vitess.Statement, len(stmts))
|
||||
for i, stmt := range stmts {
|
||||
sqlDefs[i] = stmt.AST.String()
|
||||
err := framework.ReplaceFunctionColumn(stmt.AST, paramMap)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
// stmt.AST is updated at this point with FunctionColumn
|
||||
vitessASTs[i], err = Convert(stmt)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
return strings.Join(sqlDefs, ";"), vitessASTs, nil
|
||||
}
|
||||
|
||||
// validateRoutineOptions ensures that each option is defined only once. Returns a map containing all options, or an
|
||||
// error if an option is invalid or is defined multiple times.
|
||||
func validateRoutineOptions(ctx *Context, options []tree.RoutineOption) (map[tree.FunctionOption]tree.RoutineOption, error) {
|
||||
var optDefined = make(map[tree.FunctionOption]tree.RoutineOption)
|
||||
for _, opt := range options {
|
||||
if _, ok := optDefined[opt.OptionType]; ok {
|
||||
return nil, errors.Errorf("ERROR: conflicting or redundant options")
|
||||
} else {
|
||||
optDefined[opt.OptionType] = opt
|
||||
}
|
||||
}
|
||||
return optDefined, nil
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateIndex handles *tree.CreateIndex nodes.
|
||||
func nodeCreateIndex(ctx *Context, node *tree.CreateIndex) (*vitess.AlterTable, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Concurrently {
|
||||
return nil, errors.Errorf("concurrent index creation is not yet supported")
|
||||
}
|
||||
if node.Using != "" && strings.ToLower(node.Using) != "btree" {
|
||||
return nil, errors.Errorf("index method %s is not yet supported", node.Using)
|
||||
}
|
||||
indexDef, err := nodeIndexTableDef(ctx, &tree.IndexTableDef{
|
||||
Name: node.Name,
|
||||
Columns: node.Columns,
|
||||
IndexParams: node.IndexParams,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tableName, err := nodeTableName(ctx, &node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var indexType string
|
||||
if node.Unique {
|
||||
indexType = vitess.UniqueStr
|
||||
}
|
||||
var predicate vitess.Expr
|
||||
if node.Predicate != nil {
|
||||
predicate, err = nodeExpr(ctx, node.Predicate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
Statements: []*vitess.DDL{
|
||||
{
|
||||
Action: vitess.AlterStr,
|
||||
Table: tableName,
|
||||
IfNotExists: node.IfNotExists,
|
||||
IndexSpec: &vitess.IndexSpec{
|
||||
Action: vitess.CreateStr,
|
||||
FromName: indexDef.Info.Name,
|
||||
ToName: indexDef.Info.Name,
|
||||
Type: indexType,
|
||||
Fields: indexDef.Fields,
|
||||
Options: indexDef.Options,
|
||||
Predicate: predicate,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateMaterializedView handles *tree.CreateMaterializedView nodes.
|
||||
func nodeCreateMaterializedView(ctx *Context, node *tree.CreateMaterializedView) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CREATE MATERIALIZED VIEW is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/procedures"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/parser"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
)
|
||||
|
||||
// nodeCreateProcedure handles *tree.CreateProcedure nodes.
|
||||
func nodeCreateProcedure(ctx *Context, node *tree.CreateProcedure) (vitess.Statement, error) {
|
||||
options, err := validateRoutineOptions(ctx, node.Options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Grab the general information that we'll need to create the procedure
|
||||
tableName := node.Name.ToTableName()
|
||||
params := make([]pgnodes.RoutineParam, len(node.Args))
|
||||
var defaults []vitess.Expr
|
||||
for i, arg := range node.Args {
|
||||
// parameter name
|
||||
params[i].Name = arg.Name.String()
|
||||
// parameter type
|
||||
_, params[i].Type, err = nodeResolvableTypeReference(ctx, arg.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// parameter mode
|
||||
switch arg.Mode {
|
||||
case tree.RoutineArgModeIn:
|
||||
params[i].Mode = procedures.ParameterMode_IN
|
||||
case tree.RoutineArgModeVariadic:
|
||||
params[i].Mode = procedures.ParameterMode_VARIADIC
|
||||
case tree.RoutineArgModeOut:
|
||||
params[i].Mode = procedures.ParameterMode_OUT
|
||||
case tree.RoutineArgModeInout:
|
||||
params[i].Mode = procedures.ParameterMode_INOUT
|
||||
default:
|
||||
return nil, errors.Newf("unknown procedure argmode: `%v`", arg.Mode)
|
||||
}
|
||||
// parameter default
|
||||
if arg.Default != nil {
|
||||
params[i].HasDefault = true
|
||||
d, err := nodeExpr(ctx, arg.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaults = append(defaults, d)
|
||||
}
|
||||
}
|
||||
// We only support PL/pgSQL, SQL and C for now, so we verify that here
|
||||
var parsedBody []plpgsql.InterpreterOperation
|
||||
var sqlDef string
|
||||
var sqlDefParsedStmts []vitess.Statement
|
||||
var extensionName, extensionSymbol string
|
||||
if languageOption, ok := options[tree.OptionLanguage]; ok {
|
||||
switch strings.ToLower(languageOption.Language) {
|
||||
case "plpgsql":
|
||||
// PL/pgSQL is different from standard Postgres SQL, so we have to use a special parser to handle it.
|
||||
// This parser also requires the full `CREATE PROCEDURE` string, so we'll pass that.
|
||||
parsedBody, err = plpgsql.Parse(ctx.originalQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// parse types
|
||||
for i, op := range parsedBody {
|
||||
switch op.OpCode {
|
||||
case plpgsql.OpCode_Declare:
|
||||
// ParseType uses casting to parse the given type, but
|
||||
// some special types cannot be cast. Eg: `user_defined_table_type%ROWTYPE`
|
||||
if declareTyp, err := parser.ParseType(op.PrimaryData); err == nil {
|
||||
if _, dt, err := nodeResolvableTypeReference(ctx, declareTyp, false); err == nil && dt != nil {
|
||||
dtName := dt.Name()
|
||||
if dt.Schema() != "" {
|
||||
dtName = fmt.Sprintf("%s.%s", dt.Schema(), dtName)
|
||||
}
|
||||
parsedBody[i].PrimaryData = dtName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "sql":
|
||||
as, ok := options[tree.OptionAs1]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("CREATE PROCEDURE definition needed for LANGUAGE SQL")
|
||||
}
|
||||
sqlDef, sqlDefParsedStmts, err = handleLanguageSQLAs(as.Definition, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "c":
|
||||
symbolOption, ok := options[tree.OptionAs2]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("LANGUAGE C is only supported when providing both the module name and symbol")
|
||||
}
|
||||
extensionName = symbolOption.ObjFile
|
||||
extensionSymbol = symbolOption.LinkSymbol
|
||||
default:
|
||||
return nil, errors.Errorf("CREATE PROCEDURE only supports PL/pgSQL, C and SQL for now; others are not yet supported")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Errorf("CREATE PROCEDURE does not define an input language")
|
||||
}
|
||||
// Returns the stored procedure call with all options
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateProcedure(
|
||||
tableName.Table(),
|
||||
tableName.Schema(),
|
||||
node.Replace,
|
||||
params,
|
||||
ctx.originalQuery,
|
||||
extensionName,
|
||||
extensionSymbol,
|
||||
parsedBody,
|
||||
sqlDef,
|
||||
sqlDefParsedStmts,
|
||||
),
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_SchemaIdentifiers,
|
||||
TargetNames: []string{tableName.Catalog(), tableName.Schema()},
|
||||
},
|
||||
Children: defaults,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeCreateRole handles *tree.CreateRole nodes.
|
||||
func nodeCreateRole(ctx *Context, node *tree.CreateRole) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Name) == 0 {
|
||||
// The parser should make this impossible, but extra error checking is never bad
|
||||
return nil, errors.New(`role name cannot be empty`)
|
||||
}
|
||||
switch node.Name {
|
||||
case `public`:
|
||||
return nil, errors.New(`role name "public" is reserved`)
|
||||
case `current_role`, `current_user`, `session_user`:
|
||||
return nil, errors.Errorf(`%s cannot be used as a role name here`, strings.ToUpper(node.Name))
|
||||
}
|
||||
createRole := &pgnodes.CreateRole{
|
||||
Name: node.Name,
|
||||
IfNotExists: node.IfNotExists,
|
||||
Password: "",
|
||||
IsPasswordNull: true,
|
||||
IsSuperUser: false,
|
||||
CanCreateDB: false,
|
||||
CanCreateRoles: false,
|
||||
InheritPrivileges: true,
|
||||
CanLogin: !node.IsRole,
|
||||
IsReplicationRole: false,
|
||||
CanBypassRowLevelSecurity: false,
|
||||
ConnectionLimit: -1,
|
||||
ValidUntil: "",
|
||||
IsValidUntilSet: false,
|
||||
AddToRoles: nil,
|
||||
AddAsMembers: nil,
|
||||
AddAsAdminMembers: nil,
|
||||
}
|
||||
for _, kvOption := range node.KVOptions {
|
||||
switch strings.ToUpper(string(kvOption.Key)) {
|
||||
case "BYPASSRLS":
|
||||
createRole.CanBypassRowLevelSecurity = true
|
||||
case "CONNECTION_LIMIT":
|
||||
switch value := kvOption.Value.(type) {
|
||||
case *tree.DInt:
|
||||
if value == nil {
|
||||
createRole.ConnectionLimit = -1
|
||||
} else {
|
||||
// We enforce that only int32 values will fit here in the parser
|
||||
createRole.ConnectionLimit = int32(*value)
|
||||
}
|
||||
case tree.NullLiteral:
|
||||
createRole.ConnectionLimit = -1
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
case "CREATEDB":
|
||||
createRole.CanCreateDB = true
|
||||
case "CREATEROLE":
|
||||
createRole.CanCreateRoles = true
|
||||
case "INHERIT":
|
||||
createRole.InheritPrivileges = true
|
||||
case "LOGIN":
|
||||
createRole.CanLogin = true
|
||||
case "NOBYPASSRLS":
|
||||
createRole.CanBypassRowLevelSecurity = false
|
||||
case "NOCREATEDB":
|
||||
createRole.CanCreateDB = false
|
||||
case "NOCREATEROLE":
|
||||
createRole.CanCreateRoles = false
|
||||
case "NOINHERIT":
|
||||
createRole.InheritPrivileges = false
|
||||
case "NOLOGIN":
|
||||
createRole.CanLogin = false
|
||||
case "NOREPLICATION":
|
||||
createRole.IsReplicationRole = false
|
||||
case "NOSUPERUSER":
|
||||
createRole.IsSuperUser = false
|
||||
case "PASSWORD":
|
||||
switch value := kvOption.Value.(type) {
|
||||
case *tree.DString:
|
||||
if value == nil {
|
||||
createRole.Password = ""
|
||||
createRole.IsPasswordNull = true
|
||||
} else {
|
||||
createRole.Password = string(*value)
|
||||
createRole.IsPasswordNull = false
|
||||
}
|
||||
case tree.NullLiteral:
|
||||
createRole.Password = ""
|
||||
createRole.IsPasswordNull = true
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
case "REPLICATION":
|
||||
createRole.IsReplicationRole = true
|
||||
case "SUPERUSER":
|
||||
createRole.IsSuperUser = true
|
||||
case "SYSID":
|
||||
// This is an option that is ignored by Postgres. Assuming it used to be relevant, but not any longer.
|
||||
case "VALID_UNTIL":
|
||||
strVal, ok := kvOption.Value.(*tree.DString)
|
||||
if !ok {
|
||||
return nil, errors.Errorf(`unknown role option value (%T) for option "%s"`, kvOption.Value, kvOption.Key)
|
||||
}
|
||||
if strVal == nil {
|
||||
createRole.ValidUntil = ""
|
||||
createRole.IsValidUntilSet = false
|
||||
} else {
|
||||
createRole.ValidUntil = string(*strVal)
|
||||
createRole.IsValidUntilSet = true
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf(`unknown role option "%s"`, kvOption.Key)
|
||||
}
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: createRole,
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateSchema handles *tree.CreateSchema nodes.
|
||||
func nodeCreateSchema(ctx *Context, node *tree.CreateSchema) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CREATE SCHEMA AUTHORIZATION s1 creates a schema of the same name
|
||||
schemaName := node.Schema
|
||||
if schemaName == "" {
|
||||
schemaName = node.AuthRole
|
||||
}
|
||||
|
||||
return &vitess.DBDDL{
|
||||
Action: "CREATE",
|
||||
SchemaOrDatabase: "schema",
|
||||
DBName: schemaName,
|
||||
IfNotExists: node.IfNotExists,
|
||||
CharsetCollate: nil, // TODO
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_DatabaseIdentifiers,
|
||||
TargetNames: []string{""},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// 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 (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/core/sequences"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeCreateSequence handles *tree.CreateSequence nodes.
|
||||
func nodeCreateSequence(ctx *Context, node *tree.CreateSequence) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Persistence.IsTemporary() {
|
||||
return nil, errors.Errorf("temporary sequences are not yet supported")
|
||||
}
|
||||
if node.Persistence.IsUnlogged() {
|
||||
return nil, errors.Errorf("unlogged sequences are not yet supported")
|
||||
}
|
||||
name, err := nodeTableName(ctx, &node.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(name.DbQualifier.String()) > 0 {
|
||||
return nil, errors.Errorf("CREATE SEQUENCE is currently only supported for the current database")
|
||||
}
|
||||
// Read all options and check whether they've been set (if not, we'll use the defaults)
|
||||
minValueLimit := int64(math.MinInt64)
|
||||
maxValueLimit := int64(math.MaxInt64)
|
||||
increment := int64(1)
|
||||
var minValue int64
|
||||
var maxValue int64
|
||||
var start int64
|
||||
var dataType *pgtypes.DoltgresType
|
||||
var ownerTableName string
|
||||
var ownerColumnName string
|
||||
minValueSet := false
|
||||
maxValueSet := false
|
||||
incrementSet := false
|
||||
startSet := false
|
||||
cycle := false
|
||||
fromAlter := false
|
||||
for _, option := range node.Options {
|
||||
switch option.Name {
|
||||
case tree.SeqOptAs:
|
||||
if !dataType.IsEmptyType() {
|
||||
return nil, errors.Errorf("conflicting or redundant options")
|
||||
}
|
||||
_, dataType, err = nodeResolvableTypeReference(ctx, option.AsType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch dataType.ID {
|
||||
case pgtypes.Int16.ID:
|
||||
minValueLimit = int64(math.MinInt16)
|
||||
maxValueLimit = int64(math.MaxInt16)
|
||||
case pgtypes.Int32.ID:
|
||||
minValueLimit = int64(math.MinInt32)
|
||||
maxValueLimit = int64(math.MaxInt32)
|
||||
case pgtypes.Int64.ID:
|
||||
minValueLimit = int64(math.MinInt64)
|
||||
maxValueLimit = int64(math.MaxInt64)
|
||||
default:
|
||||
return nil, errors.Errorf("sequence type must be smallint, integer, or bigint")
|
||||
}
|
||||
case tree.SeqOptCycle:
|
||||
cycle = true
|
||||
case tree.SeqOptNoCycle:
|
||||
cycle = false
|
||||
case tree.SeqOptOwnedBy:
|
||||
expr, err := nodeExpr(ctx, option.ColumnItemVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
colName, ok := expr.(*vitess.ColName)
|
||||
if !ok {
|
||||
return nil, errors.New("expected sequence owner to be a table and column name")
|
||||
}
|
||||
if colName.Qualifier.SchemaQualifier.String() != name.SchemaQualifier.String() {
|
||||
return nil, errors.New("CREATE SEQUENCE must use the same schema for the sequence and owned table")
|
||||
}
|
||||
if len(colName.Qualifier.DbQualifier.String()) > 0 {
|
||||
return nil, errors.New("database specification is not yet supported for sequences")
|
||||
}
|
||||
ownerTableName = colName.Qualifier.Name.String()
|
||||
ownerColumnName = colName.Name.String()
|
||||
case tree.SeqOptCache:
|
||||
// TODO: implement caching
|
||||
if *option.IntVal != 1 {
|
||||
return nil, errors.Errorf("sequence caching for values other than 1 are not yet supported")
|
||||
}
|
||||
case tree.SeqOptIncrement:
|
||||
increment = *option.IntVal
|
||||
if incrementSet {
|
||||
return nil, errors.Errorf("conflicting or redundant options")
|
||||
}
|
||||
if increment == 0 {
|
||||
return nil, errors.Errorf("INCREMENT must not be zero")
|
||||
}
|
||||
incrementSet = true
|
||||
case tree.SeqOptMinValue:
|
||||
if option.IntVal != nil {
|
||||
minValue = *option.IntVal
|
||||
if minValueSet {
|
||||
return nil, errors.Errorf("conflicting or redundant options")
|
||||
}
|
||||
minValueSet = true
|
||||
}
|
||||
case tree.SeqOptMaxValue:
|
||||
if option.IntVal != nil {
|
||||
maxValue = *option.IntVal
|
||||
if maxValueSet {
|
||||
return nil, errors.Errorf("conflicting or redundant options")
|
||||
}
|
||||
maxValueSet = true
|
||||
}
|
||||
case tree.SeqOptStart:
|
||||
start = *option.IntVal
|
||||
if startSet {
|
||||
return nil, errors.Errorf("conflicting or redundant options")
|
||||
}
|
||||
startSet = true
|
||||
case tree.SeqOptViaAlterTable:
|
||||
fromAlter = true
|
||||
default:
|
||||
return nil, errors.Errorf("unknown CREATE SEQUENCE option")
|
||||
}
|
||||
}
|
||||
// Determine what all values should be based on what was set and what is inferred, as well as perform
|
||||
// validation for options that make sense
|
||||
if minValueSet {
|
||||
if minValue < minValueLimit || minValue > maxValueLimit {
|
||||
return nil, errors.Errorf("MINVALUE (%d) is out of range for sequence data type %s", minValue, dataType.String())
|
||||
}
|
||||
} else if increment > 0 {
|
||||
minValue = 1
|
||||
} else {
|
||||
minValue = minValueLimit
|
||||
}
|
||||
if maxValueSet {
|
||||
if maxValue < minValueLimit || maxValue > maxValueLimit {
|
||||
return nil, errors.Errorf("MAXVALUE (%d) is out of range for sequence data type %s", maxValue, dataType.String())
|
||||
}
|
||||
} else if increment > 0 {
|
||||
maxValue = maxValueLimit
|
||||
} else {
|
||||
maxValue = -1
|
||||
}
|
||||
if startSet {
|
||||
if start < minValue {
|
||||
return nil, errors.Errorf("START value (%d) cannot be less than MINVALUE (%d))", start, minValue)
|
||||
}
|
||||
if start > maxValue {
|
||||
return nil, errors.Errorf("START value (%d) cannot be greater than MAXVALUE (%d)", start, maxValue)
|
||||
}
|
||||
} else if increment > 0 {
|
||||
start = minValue
|
||||
} else {
|
||||
start = maxValue
|
||||
}
|
||||
if dataType.IsEmptyType() {
|
||||
dataType = pgtypes.Int64
|
||||
}
|
||||
// Returns the stored procedure call with all options
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateSequence(node.IfNotExists, name.SchemaQualifier.String(), fromAlter, &sequences.Sequence{
|
||||
Id: id.NewSequence("", name.Name.String()),
|
||||
DataTypeID: dataType.ID,
|
||||
Persistence: sequences.Persistence_Permanent,
|
||||
Start: start,
|
||||
Current: start,
|
||||
Increment: increment,
|
||||
Minimum: minValue,
|
||||
Maximum: maxValue,
|
||||
Cache: 1,
|
||||
Cycle: cycle,
|
||||
IsAtEnd: false,
|
||||
OwnerTable: id.NewTable("", ownerTableName),
|
||||
OwnerColumn: ownerColumnName,
|
||||
}),
|
||||
Children: nil,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_SchemaIdentifiers,
|
||||
TargetNames: []string{name.DbQualifier.String(), name.SchemaQualifier.String()},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateStats handles *tree.CreateStats nodes.
|
||||
func nodeCreateStats(ctx *Context, node *tree.CreateStats) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("CREATE STATISTICS is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeCreateTable handles *tree.CreateTable nodes.
|
||||
func nodeCreateTable(ctx *Context, node *tree.CreateTable) (*vitess.DDL, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.StorageParams) > 0 {
|
||||
return nil, errors.Errorf("storage parameters are not yet supported")
|
||||
}
|
||||
// TODO: support tree.CreateTableOnCommitDrop and tree.CreateTableOnCommitDeleteRows
|
||||
switch node.OnCommit {
|
||||
case tree.CreateTableOnCommitDrop:
|
||||
// is unsupported and ignored
|
||||
case tree.CreateTableOnCommitDeleteRows:
|
||||
// is unsupported and ignored
|
||||
}
|
||||
tableName, err := nodeTableName(ctx, &node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var isTemporary bool
|
||||
switch node.Persistence {
|
||||
case tree.PersistencePermanent:
|
||||
isTemporary = false
|
||||
case tree.PersistenceTemporary:
|
||||
isTemporary = true
|
||||
case tree.PersistenceUnlogged:
|
||||
return nil, errors.Errorf("UNLOGGED is not yet supported")
|
||||
default:
|
||||
return nil, errors.Errorf("unknown persistence strategy encountered")
|
||||
}
|
||||
var optSelect *vitess.OptSelect
|
||||
if node.Using != "" {
|
||||
return nil, errors.Errorf("USING is not yet supported")
|
||||
}
|
||||
if node.Tablespace != "" {
|
||||
return nil, errors.Errorf("TABLESPACE is not yet supported")
|
||||
}
|
||||
if node.AsSource != nil {
|
||||
selectStmt, err := nodeSelect(ctx, node.AsSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optSelect = &vitess.OptSelect{
|
||||
Select: selectStmt,
|
||||
}
|
||||
}
|
||||
var optLike *vitess.OptLike
|
||||
if len(node.Inherits) > 0 {
|
||||
optLike = &vitess.OptLike{
|
||||
LikeTables: []vitess.TableName{},
|
||||
}
|
||||
for _, table := range node.Inherits {
|
||||
likeTable, err := nodeTableName(ctx, &table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
optLike.LikeTables = append(optLike.LikeTables, likeTable)
|
||||
}
|
||||
}
|
||||
if node.WithNoData {
|
||||
return nil, errors.Errorf("WITH NO DATA is not yet supported")
|
||||
}
|
||||
ddl := &vitess.DDL{
|
||||
Action: vitess.CreateStr,
|
||||
Table: tableName,
|
||||
IfNotExists: node.IfNotExists,
|
||||
Temporary: isTemporary,
|
||||
OptSelect: optSelect,
|
||||
OptLike: optLike,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_CREATE,
|
||||
TargetType: auth.AuthTargetType_SchemaIdentifiers,
|
||||
TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String()},
|
||||
},
|
||||
}
|
||||
if err = assignTableDefs(ctx, node.Defs, ddl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if node.PartitionBy != nil {
|
||||
switch node.PartitionBy.Type {
|
||||
case tree.PartitionByList:
|
||||
if len(node.PartitionBy.Elems) != 1 {
|
||||
return nil, errors.Errorf("PARTITION BY LIST must have a single column or expression")
|
||||
}
|
||||
}
|
||||
|
||||
// GMS does not support PARTITION BY, so we parse it and ignore it
|
||||
if ddl.TableSpec != nil {
|
||||
ddl.TableSpec.PartitionOpt = &vitess.PartitionOption{
|
||||
PartitionType: string(node.PartitionBy.Type),
|
||||
Expr: vitess.NewColName(string(node.PartitionBy.Elems[0].Column)),
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.PartitionOf.Table() != "" {
|
||||
return nil, errors.Errorf("PARTITION OF is not yet supported")
|
||||
}
|
||||
return ddl, nil
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/core/triggers"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
)
|
||||
|
||||
// createTriggerWhenCapture is a regex that should only capture the contents of the WHEN expression. Although a bit
|
||||
// complex, this is done to ensure that the capture group contains only the WHEN expression and nothing else.
|
||||
var createTriggerWhenCapture = regexp.MustCompile(`(?is)create\s+(?:or\s+replace\s+)?(?:constraint\s+)?trigger\s+.*\s+for\s+(?:each\s+)?(?:row|statement)\s+when\s+\((.*)\)\s+execute\s+(?:function|procedure).*`)
|
||||
|
||||
// nodeCreateTrigger handles *tree.CreateTrigger nodes.
|
||||
func nodeCreateTrigger(ctx *Context, node *tree.CreateTrigger) (_ vitess.Statement, err error) {
|
||||
if node.Constraint {
|
||||
return NotYetSupportedError("CREATE CONSTRAINT TRIGGER is not yet supported")
|
||||
}
|
||||
if !node.RefTable.IsEmpty() {
|
||||
return NotYetSupportedError("FROM is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
if node.Deferrable != tree.TriggerNotDeferrable {
|
||||
return NotYetSupportedError("DEFERRABLE is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
if len(node.Relations) > 0 {
|
||||
return NotYetSupportedError("REFERENCING is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
if !node.ForEachRow {
|
||||
return NotYetSupportedError("FOR EACH STATEMENT is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
funcName := node.FuncName.ToTableName()
|
||||
var timing triggers.TriggerTiming
|
||||
switch node.Time {
|
||||
case tree.TriggerTimeBefore:
|
||||
timing = triggers.TriggerTiming_Before
|
||||
case tree.TriggerTimeAfter:
|
||||
timing = triggers.TriggerTiming_After
|
||||
case tree.TriggerTimeInsteadOf:
|
||||
return NotYetSupportedError("INSTEAD OF is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
var events []triggers.TriggerEvent
|
||||
for _, event := range node.Events {
|
||||
switch event.Type {
|
||||
case tree.TriggerEventInsert:
|
||||
events = append(events, triggers.TriggerEvent{
|
||||
Type: triggers.TriggerEventType_Insert,
|
||||
})
|
||||
case tree.TriggerEventUpdate:
|
||||
if len(event.Cols) > 0 {
|
||||
return NotYetSupportedError("UPDATE specific columns are not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
events = append(events, triggers.TriggerEvent{
|
||||
Type: triggers.TriggerEventType_Update,
|
||||
ColumnNames: event.Cols.ToStrings(),
|
||||
})
|
||||
case tree.TriggerEventDelete:
|
||||
events = append(events, triggers.TriggerEvent{
|
||||
Type: triggers.TriggerEventType_Delete,
|
||||
})
|
||||
case tree.TriggerEventTruncate:
|
||||
return NotYetSupportedError("TRUNCATE is not yet supported for CREATE TRIGGER")
|
||||
default:
|
||||
return NotYetSupportedError("UNKNOWN EVENT TYPE is not yet supported for CREATE TRIGGER")
|
||||
}
|
||||
}
|
||||
// WHEN expressions seem to behave identically to interpreted functions, so we'll parse them as interpreted functions.
|
||||
// To do this, we need the raw string, and we wrap it as though it were a trigger function (which has special logic
|
||||
// for handling NEW and OLD rows). Using a regex for this rather than modifying the parser may seem suboptimal, but
|
||||
// we want to retain the parser validation of using an expression, however we cannot rely on the expression's
|
||||
// String() function to return the **exact** same string, so we capture it with a regex.
|
||||
var whenOps []plpgsql.InterpreterOperation
|
||||
if node.When != nil {
|
||||
matches := createTriggerWhenCapture.FindStringSubmatch(ctx.originalQuery)
|
||||
if len(matches) != 2 {
|
||||
return nil, errors.New("unable to parse WHEN expression from CREATE TRIGGER")
|
||||
}
|
||||
whenOps, err = plpgsql.Parse(fmt.Sprintf(`CREATE FUNCTION when_wrapper() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
RETURN %s;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`, matches[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewCreateTrigger(
|
||||
id.NewTrigger(node.OnTable.Schema(), node.OnTable.Table(), node.Name.String()),
|
||||
id.NewFunction(funcName.Schema(), funcName.Table()),
|
||||
node.Replace,
|
||||
timing,
|
||||
events,
|
||||
node.ForEachRow,
|
||||
whenOps,
|
||||
node.Args.ToStrings(),
|
||||
ctx.originalQuery,
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeCreateType handles *tree.CreateType nodes.
|
||||
func nodeCreateType(ctx *Context, node *tree.CreateType) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
name, err := nodeUnresolvedObjectName(ctx, node.TypeName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaName := name.SchemaQualifier.String()
|
||||
typName := name.Name.String()
|
||||
var createTypeNode *pgnodes.CreateType
|
||||
switch node.Variety {
|
||||
case tree.Composite:
|
||||
typs := make([]pgnodes.CompositeAsType, len(node.Composite.Types))
|
||||
for i, t := range node.Composite.Types {
|
||||
_, dataType, err := nodeResolvableTypeReference(ctx, t.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dataType == pgtypes.Record {
|
||||
return nil, errors.Errorf(`column "%s" has pseudo-type record`, t.AttrName)
|
||||
}
|
||||
|
||||
typs[i] = pgnodes.CompositeAsType{
|
||||
AttrName: t.AttrName,
|
||||
Typ: dataType,
|
||||
Collation: t.Collate,
|
||||
}
|
||||
}
|
||||
createTypeNode = pgnodes.NewCreateCompositeType(schemaName, typName, typs)
|
||||
case tree.Enum:
|
||||
createTypeNode = pgnodes.NewCreateEnumType(schemaName, typName, node.Enum.Labels)
|
||||
case tree.Range:
|
||||
return nil, errors.Errorf("CREATE RANGE TYPE is not yet supported")
|
||||
case tree.Base:
|
||||
return nil, errors.Errorf("CREATE BASE TYPE is not yet supported")
|
||||
case tree.Shell:
|
||||
createTypeNode = pgnodes.NewCreateShellType(schemaName, typName)
|
||||
case tree.Domain:
|
||||
// NOT POSSIBLE
|
||||
return nil, errors.Errorf("use CREATE DOMAIN to create domain type")
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: createTypeNode,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeCreateView handles *tree.CreateView nodes.
|
||||
func nodeCreateView(ctx *Context, node *tree.CreateView) (*vitess.DDL, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Persistence.IsTemporary() {
|
||||
return nil, errors.Errorf("CREATE TEMPORARY VIEW is not yet supported")
|
||||
}
|
||||
if node.IsRecursive {
|
||||
return nil, errors.Errorf("CREATE RECURSIVE VIEW is not yet supported")
|
||||
}
|
||||
var checkOption = tree.ViewCheckOptionUnspecified
|
||||
var sqlSecurity string
|
||||
if node.Options != nil {
|
||||
for _, opt := range node.Options {
|
||||
switch strings.ToLower(opt.Name) {
|
||||
case "check_option":
|
||||
switch strings.ToLower(opt.CheckOpt) {
|
||||
case "local":
|
||||
checkOption = tree.ViewCheckOptionLocal
|
||||
case "cascaded":
|
||||
checkOption = tree.ViewCheckOptionCascaded
|
||||
default:
|
||||
return nil, errors.Errorf(`"ERROR: syntax error at or near "%s"`, opt.Name)
|
||||
}
|
||||
case "security_barrier":
|
||||
if opt.Security {
|
||||
return nil, errors.Errorf("CREATE VIEW '%s' = true option is not yet supported", opt.Name)
|
||||
}
|
||||
case "security_invoker":
|
||||
if opt.Security {
|
||||
sqlSecurity = "invoker"
|
||||
} else {
|
||||
sqlSecurity = "definer"
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf(`"ERROR: syntax error at or near "%s"`, opt.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checkOption != tree.ViewCheckOptionUnspecified && node.CheckOption != tree.ViewCheckOptionUnspecified {
|
||||
return nil, errors.Errorf(`ERROR: parameter "check_option" specified more than once`)
|
||||
} else {
|
||||
checkOption = node.CheckOption
|
||||
}
|
||||
|
||||
vCheckOpt := vitess.ViewCheckOptionUnspecified
|
||||
switch checkOption {
|
||||
case tree.ViewCheckOptionCascaded:
|
||||
vCheckOpt = vitess.ViewCheckOptionCascaded
|
||||
case tree.ViewCheckOptionLocal:
|
||||
vCheckOpt = vitess.ViewCheckOptionLocal
|
||||
default:
|
||||
}
|
||||
|
||||
tableName, err := nodeTableName(ctx, &node.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selectStmt, err := nodeSelect(ctx, node.AsSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cols = make(vitess.Columns, len(node.ColumnNames))
|
||||
for i, col := range node.ColumnNames {
|
||||
cols[i] = vitess.NewColIdent(col.String())
|
||||
}
|
||||
|
||||
stmt := &vitess.DDL{
|
||||
Action: vitess.CreateStr,
|
||||
OrReplace: node.Replace,
|
||||
ViewSpec: &vitess.ViewSpec{
|
||||
ViewName: tableName,
|
||||
ViewExpr: selectStmt,
|
||||
Columns: cols,
|
||||
Security: sqlSecurity,
|
||||
CheckOption: vCheckOpt,
|
||||
},
|
||||
SubStatementStr: node.AsSource.String(),
|
||||
}
|
||||
return stmt, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeDeallocate handles *tree.Deallocate nodes.
|
||||
func nodeDeallocate(ctx *Context, node *tree.Deallocate) (*vitess.Deallocate, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &vitess.Deallocate{
|
||||
Name: string(node.Name),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
)
|
||||
|
||||
// nodeDelete handles *tree.Delete nodes.
|
||||
func nodeDelete(ctx *Context, node *tree.Delete) (*vitess.Delete, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ctx.Auth().PushAuthType(auth.AuthType_DELETE)
|
||||
defer ctx.Auth().PopAuthType()
|
||||
|
||||
var returningExprs vitess.SelectExprs
|
||||
if returning, ok := node.Returning.(*tree.ReturningExprs); ok {
|
||||
var err error
|
||||
returningExprs, err = nodeSelectExprs(ctx, tree.SelectExprs(*returning))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
with, err := nodeWith(ctx, node.With)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table, err := nodeTableExpr(ctx, node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
where, err := nodeWhere(ctx, node.Where)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderBy, err := nodeOrderBy(ctx, node.OrderBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limit, err := nodeLimit(ctx, node.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.Delete{
|
||||
TableExprs: vitess.TableExprs{table},
|
||||
With: with,
|
||||
Where: where,
|
||||
OrderBy: orderBy,
|
||||
Limit: limit,
|
||||
Returning: returningExprs,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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/node"
|
||||
)
|
||||
|
||||
// nodeDiscard handles *tree.Discard nodes.
|
||||
func nodeDiscard(ctx *Context, discard *tree.Discard) (vitess.Statement, error) {
|
||||
if discard == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if discard.Mode != tree.DiscardModeAll {
|
||||
return nil, errors.Errorf("unhandled DISCARD mode: %v", discard.Mode)
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: node.DiscardStatement{},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeDropAggregate handles *tree.DropAggregate nodes.
|
||||
func nodeDropAggregate(ctx *Context, node *tree.DropAggregate) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !ignoreUnsupportedStatements {
|
||||
for _, agg := range node.Aggregates {
|
||||
if err := validateAggArgMode(ctx, agg.AggSig.Args, agg.AggSig.OrderByArgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotYetSupportedError("DROP AGGREGATE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropCast handles *tree.DropCast nodes.
|
||||
func nodeDropCast(ctx *Context, node *tree.DropCast) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
_, sourceType, err := nodeResolvableTypeReference(ctx, node.Source, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, targetType, err := nodeResolvableTypeReference(ctx, node.Target, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropCast(
|
||||
sourceType,
|
||||
targetType,
|
||||
node.IfExists,
|
||||
),
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_DELETE,
|
||||
TargetType: auth.AuthTargetType_TODO,
|
||||
TargetNames: []string{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeDropDatabase handles *tree.DropDatabase nodes.
|
||||
func nodeDropDatabase(_ *Context, node *tree.DropDatabase) (*vitess.DBDDL, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Force {
|
||||
return nil, errors.Errorf("WITH ( FORCE ) is not yet supported")
|
||||
}
|
||||
return &vitess.DBDDL{
|
||||
Action: vitess.DropStr,
|
||||
SchemaOrDatabase: "database",
|
||||
DBName: bareIdentifier(node.Name),
|
||||
IfExists: node.IfExists,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropDomain handles *tree.DropDomain nodes.
|
||||
func nodeDropDomain(ctx *Context, node *tree.DropDomain) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Names) != 1 {
|
||||
return nil, errors.Errorf("dropping multiple domains in DROP DOMAIN is not yet supported")
|
||||
}
|
||||
name, err := nodeTableName(ctx, &node.Names[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropDomain(
|
||||
node.IfExists,
|
||||
name.DbQualifier.String(),
|
||||
name.SchemaQualifier.String(),
|
||||
name.Name.String(),
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeDropExtension handles *tree.DropExtension nodes.
|
||||
func nodeDropExtension(ctx *Context, node *tree.DropExtension) (vitess.Statement, error) {
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropExtension(
|
||||
node.Names.ToStrings(),
|
||||
node.IfExists,
|
||||
node.DropBehavior == tree.DropCascade,
|
||||
),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2025 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 (
|
||||
"fmt"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropFunction handles *tree.DropFunction nodes.
|
||||
func nodeDropFunction(ctx *Context, node *tree.DropFunction) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if node.DropBehavior == tree.DropCascade {
|
||||
return nil, fmt.Errorf("DROP FUNCTION with CASCADE is not supported yet")
|
||||
}
|
||||
|
||||
if len(node.Functions) == 0 {
|
||||
return nil, fmt.Errorf("no function name specified for DROP FUNCTION")
|
||||
}
|
||||
|
||||
functions := make([]*pgnodes.RoutineWithParams, len(node.Functions))
|
||||
for i, fn := range node.Functions {
|
||||
var args []pgnodes.RoutineParam
|
||||
for _, a := range fn.Args {
|
||||
if a.Mode != tree.RoutineArgModeOut {
|
||||
_, dt, err := nodeResolvableTypeReference(ctx, a.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, pgnodes.RoutineParam{
|
||||
Name: a.Name.String(),
|
||||
Type: dt,
|
||||
})
|
||||
}
|
||||
}
|
||||
objName := fn.Name.ToTableName()
|
||||
functions[i] = &pgnodes.RoutineWithParams{
|
||||
Args: args,
|
||||
SchemaName: objName.Schema(),
|
||||
RoutineName: objName.Object(),
|
||||
}
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropFunction(
|
||||
node.IfExists,
|
||||
functions,
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeDropIndex handles *tree.DropIndex nodes.
|
||||
func nodeDropIndex(ctx *Context, node *tree.DropIndex) (*vitess.AlterTable, error) {
|
||||
if node == nil || len(node.IndexList) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
// Default behavior, nothing to do
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("RESTRICT is not yet supported")
|
||||
case tree.DropCascade:
|
||||
return nil, errors.Errorf("CASCADE is not yet supported")
|
||||
}
|
||||
if len(node.IndexList) > 1 {
|
||||
return nil, errors.Errorf("multi-index dropping is not yet supported")
|
||||
}
|
||||
if node.Concurrently {
|
||||
return nil, errors.Errorf("concurrent indexes are not yet supported")
|
||||
}
|
||||
var tableName vitess.TableName
|
||||
ddls := make([]*vitess.DDL, len(node.IndexList))
|
||||
for i, index := range node.IndexList {
|
||||
newTableName, err := nodeTableName(ctx, &index.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !tableName.Name.IsEmpty() && tableName.String() != newTableName.String() {
|
||||
return nil, errors.Errorf("only dropping indexes from the same table is currently supported")
|
||||
}
|
||||
tableName = newTableName
|
||||
ddls[i] = &vitess.DDL{
|
||||
Action: vitess.AlterStr,
|
||||
Table: tableName,
|
||||
IfExists: node.IfExists,
|
||||
IndexSpec: &vitess.IndexSpec{
|
||||
Action: vitess.DropStr,
|
||||
FromName: vitess.NewColIdent(string(index.Index)),
|
||||
ToName: vitess.NewColIdent(string(index.Index)),
|
||||
},
|
||||
}
|
||||
}
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
Statements: ddls,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2025 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 (
|
||||
"fmt"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropProcedure handles *tree.DropProcedure nodes.
|
||||
func nodeDropProcedure(ctx *Context, node *tree.DropProcedure) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if node.DropBehavior == tree.DropCascade {
|
||||
return nil, fmt.Errorf("DROP PROCEDURE with CASCADE is not supported yet")
|
||||
}
|
||||
|
||||
if len(node.Procedures) == 0 {
|
||||
return nil, fmt.Errorf("no function name specified for DROP PROCEDURE")
|
||||
}
|
||||
|
||||
procedures := make([]*pgnodes.RoutineWithParams, len(node.Procedures))
|
||||
for i, fn := range node.Procedures {
|
||||
var args []pgnodes.RoutineParam
|
||||
for _, a := range fn.Args {
|
||||
if a.Mode != tree.RoutineArgModeOut {
|
||||
_, dt, err := nodeResolvableTypeReference(ctx, a.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, pgnodes.RoutineParam{
|
||||
Name: a.Name.String(),
|
||||
Type: dt,
|
||||
})
|
||||
}
|
||||
}
|
||||
objName := fn.Name.ToTableName()
|
||||
procedures[i] = &pgnodes.RoutineWithParams{
|
||||
Args: args,
|
||||
SchemaName: objName.Schema(),
|
||||
RoutineName: objName.Object(),
|
||||
}
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropProcedure(
|
||||
node.IfExists,
|
||||
procedures,
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropRole handles *tree.DropRole nodes.
|
||||
func nodeDropRole(ctx *Context, node *tree.DropRole) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var names []string
|
||||
for _, name := range node.Names {
|
||||
switch name := name.(type) {
|
||||
case *tree.StrVal:
|
||||
names = append(names, name.RawString())
|
||||
default:
|
||||
return nil, errors.Errorf("unknown type `%T` for DROP ROLE name", name)
|
||||
}
|
||||
}
|
||||
// Rather than account for every string type for error checking, we can just do it in a second loop
|
||||
for _, name := range names {
|
||||
switch name {
|
||||
case `public`, `current_role`, `current_user`, `session_user`:
|
||||
return nil, errors.New("cannot use special role specifier in DROP ROLE")
|
||||
}
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.DropRole{
|
||||
Names: names,
|
||||
IfExists: node.IfExists,
|
||||
},
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeDropSchema handles *tree.DropSchema nodes.
|
||||
func nodeDropSchema(ctx *Context, node *tree.DropSchema) (vitess.Statement, error) {
|
||||
// TODO: disallow dropping pg_catalog for now
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(node.Names) > 1 {
|
||||
return NotYetSupportedError("DROP SCHEMA with multiple schema names is not yet supported.")
|
||||
}
|
||||
|
||||
if node.DropBehavior == tree.DropCascade {
|
||||
return NotYetSupportedError("DROP SCHEMA with CASCADE behavior is not yet supported.")
|
||||
}
|
||||
|
||||
schemaName := node.Names[0]
|
||||
|
||||
return &vitess.DBDDL{
|
||||
Action: vitess.DropStr,
|
||||
SchemaOrDatabase: "schema",
|
||||
DBName: schemaName,
|
||||
CharsetCollate: nil,
|
||||
IfExists: node.IfExists,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_DELETE,
|
||||
TargetType: auth.AuthTargetType_SchemaIdentifiers,
|
||||
TargetNames: []string{"", schemaName},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropSequence handles *tree.DropSequence nodes.
|
||||
func nodeDropSequence(ctx *Context, node *tree.DropSequence) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Names) != 1 {
|
||||
return nil, errors.Errorf("dropping multiple sequences in DROP SEQUENCE is not yet supported")
|
||||
}
|
||||
name, err := nodeTableName(ctx, &node.Names[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(name.DbQualifier.String()) > 0 {
|
||||
return nil, errors.Errorf("DROP SEQUENCE is currently only supported for the current database")
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropSequence(node.IfExists, name.SchemaQualifier.String(), name.Name.String(),
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeDropTable handles *tree.DropTable nodes.
|
||||
func nodeDropTable(ctx *Context, node *tree.DropTable) (*vitess.DDL, error) {
|
||||
if node == nil || len(node.Names) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
// Default behavior, nothing to do
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("RESTRICT is not yet supported")
|
||||
case tree.DropCascade:
|
||||
return nil, errors.Errorf("CASCADE is not yet supported")
|
||||
}
|
||||
tableNames := make([]vitess.TableName, len(node.Names))
|
||||
authTableNames := make([]string, 0, len(node.Names)*3)
|
||||
for i := range node.Names {
|
||||
var err error
|
||||
tableNames[i], err = nodeTableName(ctx, &node.Names[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authTableNames = append(authTableNames,
|
||||
tableNames[i].DbQualifier.String(), tableNames[i].SchemaQualifier.String(), tableNames[i].Name.String())
|
||||
}
|
||||
return &vitess.DDL{
|
||||
Action: vitess.DropStr,
|
||||
FromTables: tableNames,
|
||||
IfExists: node.IfExists,
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_DROPTABLE,
|
||||
TargetType: auth.AuthTargetType_TableIdentifiers,
|
||||
TargetNames: authTableNames,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropTrigger handles *tree.DropTrigger nodes.
|
||||
func nodeDropTrigger(ctx *Context, node *tree.DropTrigger) (vitess.Statement, error) {
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
// Default behavior, nothing to do
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("RESTRICT is not yet supported")
|
||||
case tree.DropCascade:
|
||||
return nil, errors.Errorf("CASCADE is not yet supported")
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropTrigger(
|
||||
node.IfExists,
|
||||
node.Name.String(),
|
||||
node.OnTable.Schema(),
|
||||
node.OnTable.Table(),
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeDropType handles *tree.DropType nodes.
|
||||
func nodeDropType(ctx *Context, node *tree.DropType) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(node.Names) != 1 {
|
||||
return nil, errors.Errorf("dropping multiple types in DROP TYPE is not yet supported")
|
||||
}
|
||||
tn := node.Names[0].ToTableName()
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewDropType(
|
||||
node.IfExists,
|
||||
tn.Catalog(),
|
||||
tn.Schema(),
|
||||
tn.Object(),
|
||||
node.DropBehavior == tree.DropCascade),
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeDropView handles *tree.DropView nodes.
|
||||
func nodeDropView(ctx *Context, node *tree.DropView) (*vitess.DDL, error) {
|
||||
if node == nil || len(node.Names) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
switch node.DropBehavior {
|
||||
case tree.DropDefault:
|
||||
// Default behavior, nothing to do
|
||||
case tree.DropRestrict:
|
||||
return nil, errors.Errorf("RESTRICT is not yet supported")
|
||||
case tree.DropCascade:
|
||||
return nil, errors.Errorf("CASCADE is not yet supported")
|
||||
}
|
||||
tableNames := make([]vitess.TableName, len(node.Names))
|
||||
for i := range node.Names {
|
||||
var err error
|
||||
tableNames[i], err = nodeTableName(ctx, &node.Names[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
//TODO: handle IsMaterialized
|
||||
return &vitess.DDL{
|
||||
Action: vitess.DropStr,
|
||||
IfExists: node.IfExists,
|
||||
FromViews: tableNames,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeExecute handles *tree.Execute nodes.
|
||||
func nodeExecute(ctx *Context, node *tree.Execute) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("EXECUTE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeExplain handles *tree.Explain nodes.
|
||||
func nodeExplain(ctx *Context, node *tree.Explain) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if node.TableName != nil {
|
||||
tableName, err := nodeUnresolvedObjectName(ctx, node.TableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var showTableOpts *vitess.ShowTablesOpt
|
||||
if node.AsOf != nil {
|
||||
asOf, err := nodeExpr(ctx, node.AsOf.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
showTableOpts = &vitess.ShowTablesOpt{
|
||||
AsOf: asOf,
|
||||
SchemaName: tableName.SchemaQualifier.String(),
|
||||
DbName: tableName.DbQualifier.String(),
|
||||
}
|
||||
}
|
||||
|
||||
show := &vitess.Show{
|
||||
Type: "columns",
|
||||
Table: tableName,
|
||||
ShowTablesOpt: showTableOpts,
|
||||
}
|
||||
|
||||
return show, nil
|
||||
}
|
||||
|
||||
if stmt, ok := node.Statement.(*tree.Select); ok {
|
||||
// TODO: read tree.ExplainOptions
|
||||
selectStmt, err := nodeSelect(ctx, stmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
explain := &vitess.Explain{
|
||||
ExplainFormat: vitess.TreeStr,
|
||||
Statement: selectStmt,
|
||||
}
|
||||
return explain, nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("This EXPLAIN syntax is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeExplainAnalyzeDebug handles *tree.ExplainAnalyzeDebug nodes.
|
||||
func nodeExplainAnalyzeDebug(ctx *Context, node *tree.ExplainAnalyzeDebug) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("EXPLAIN ANALYZE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeExport handles *tree.Export nodes.
|
||||
func nodeExport(ctx *Context, node *tree.Export) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("EXPORT is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,937 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/types"
|
||||
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeExprs handles tree.Exprs nodes.
|
||||
func nodeExprs(ctx *Context, node tree.Exprs) (vitess.Exprs, error) {
|
||||
if len(node) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
exprs := make(vitess.Exprs, len(node))
|
||||
for i := range node {
|
||||
var err error
|
||||
if exprs[i], err = nodeExpr(ctx, node[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return exprs, nil
|
||||
}
|
||||
|
||||
// nodeCompositeDatum handles tree.CompositeDatum nodes.
|
||||
func nodeCompositeDatum(ctx *Context, node tree.CompositeDatum) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeConstant handles tree.Constant nodes.
|
||||
func nodeConstant(ctx *Context, node tree.Constant) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeDatum handles tree.Datum nodes.
|
||||
func nodeDatum(ctx *Context, node tree.Datum) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeSubqueryExpr handles tree.SubqueryExpr nodes.
|
||||
func nodeSubqueryExpr(ctx *Context, node tree.SubqueryExpr) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeTypedExpr handles tree.TypedExpr nodes.
|
||||
func nodeTypedExpr(ctx *Context, node tree.TypedExpr) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeVariableExpr handles tree.VariableExpr nodes.
|
||||
func nodeVariableExpr(ctx *Context, node tree.VariableExpr) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeVarName handles tree.VarName nodes.
|
||||
func nodeVarName(ctx *Context, node tree.VarName) (vitess.Expr, error) {
|
||||
return nodeExpr(ctx, node)
|
||||
}
|
||||
|
||||
// nodeExpr handles tree.Expr nodes.
|
||||
func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
|
||||
switch node := node.(type) {
|
||||
case *tree.AllColumnsSelector:
|
||||
return nil, errors.Errorf("table.* syntax is not yet supported in this context")
|
||||
case *tree.AndExpr:
|
||||
left, err := nodeExpr(ctx, node.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := nodeExpr(ctx, node.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.AndExpr{
|
||||
Left: left,
|
||||
Right: right,
|
||||
}, nil
|
||||
case *tree.AnnotateTypeExpr:
|
||||
return nil, errors.Errorf("ANNOTATE_TYPE is not yet supported")
|
||||
case *tree.Array:
|
||||
unresolvedChildren := make([]vitess.Expr, len(node.Exprs))
|
||||
var coercedType *pgtypes.DoltgresType
|
||||
if node.HasResolvedType() {
|
||||
_, resolvedType, err := nodeResolvableTypeReference(ctx, node.ResolvedType(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resolvedType.IsArrayType() {
|
||||
coercedType = resolvedType
|
||||
} else {
|
||||
return nil, errors.Errorf("array has invalid resolved type")
|
||||
}
|
||||
}
|
||||
for i, arrayExpr := range node.Exprs {
|
||||
var err error
|
||||
unresolvedChildren[i], err = nodeExpr(ctx, arrayExpr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
arrayExpr, err := pgexprs.NewArray(coercedType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: arrayExpr,
|
||||
Children: unresolvedChildren,
|
||||
}, nil
|
||||
case *tree.ArrayFlatten:
|
||||
subquery, err := nodeExpr(ctx, node.Subquery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.ArrayFlatten{},
|
||||
Children: vitess.Exprs{subquery},
|
||||
}, nil
|
||||
case *tree.BinaryExpr:
|
||||
// We will eventually support operators in other schemas, but for now we only can handle built-ins
|
||||
if len(node.Schema) > 0 && node.Schema != "pg_catalog" {
|
||||
return nil, errors.Errorf("schema %q not allowed in OPERATOR syntax", node.Schema)
|
||||
}
|
||||
|
||||
left, err := nodeExpr(ctx, node.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := nodeExpr(ctx, node.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var operator framework.Operator
|
||||
switch node.Operator {
|
||||
case tree.Bitand:
|
||||
operator = framework.Operator_BinaryBitAnd
|
||||
case tree.Bitor:
|
||||
operator = framework.Operator_BinaryBitOr
|
||||
case tree.Bitxor:
|
||||
operator = framework.Operator_BinaryBitXor
|
||||
case tree.Plus:
|
||||
operator = framework.Operator_BinaryPlus
|
||||
case tree.Minus:
|
||||
operator = framework.Operator_BinaryMinus
|
||||
case tree.Mult:
|
||||
operator = framework.Operator_BinaryMultiply
|
||||
case tree.Div:
|
||||
operator = framework.Operator_BinaryDivide
|
||||
case tree.FloorDiv:
|
||||
// TODO: replace with floor divide function
|
||||
return nil, errors.Errorf("the floor divide operator is not yet supported")
|
||||
case tree.Mod:
|
||||
operator = framework.Operator_BinaryMod
|
||||
case tree.Pow:
|
||||
// TODO: replace with power function
|
||||
return nil, errors.Errorf("the power operator is not yet supported")
|
||||
case tree.Concat:
|
||||
operator = framework.Operator_BinaryConcatenate
|
||||
case tree.LShift:
|
||||
operator = framework.Operator_BinaryShiftLeft
|
||||
case tree.RShift:
|
||||
operator = framework.Operator_BinaryShiftRight
|
||||
case tree.JSONFetchVal:
|
||||
operator = framework.Operator_BinaryJSONExtractJson
|
||||
case tree.JSONFetchText:
|
||||
operator = framework.Operator_BinaryJSONExtractText
|
||||
case tree.JSONFetchValPath:
|
||||
operator = framework.Operator_BinaryJSONExtractPathJson
|
||||
case tree.JSONFetchTextPath:
|
||||
operator = framework.Operator_BinaryJSONExtractPathText
|
||||
default:
|
||||
return nil, errors.Errorf("the binary operator used is not yet supported")
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(operator),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case *tree.CaseExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whens := make([]*vitess.When, len(node.Whens))
|
||||
for i := range node.Whens {
|
||||
val, err := nodeExpr(ctx, node.Whens[i].Val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cond, err := nodeExpr(ctx, node.Whens[i].Cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whens[i] = &vitess.When{
|
||||
Val: val,
|
||||
Cond: cond,
|
||||
}
|
||||
}
|
||||
else_, err := nodeExpr(ctx, node.Else)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.CaseExpr{
|
||||
Expr: expr,
|
||||
Whens: whens,
|
||||
Else: else_,
|
||||
}, nil
|
||||
case *tree.CastExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch node.SyntaxMode {
|
||||
case tree.CastExplicit, tree.CastShort:
|
||||
// Both of these are acceptable
|
||||
case tree.CastPrepend:
|
||||
// used for typed literals
|
||||
strVal, isStrVal := node.Expr.(*tree.StrVal)
|
||||
t, isT := node.Type.(*types.T)
|
||||
if isStrVal && isT {
|
||||
typedExpr, err := strVal.ResolveAsType(context.TODO(), nil, t)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("cannot resolve '%s' as type %s", strVal.String(), t.Name())
|
||||
}
|
||||
expr, err = nodeExpr(ctx, typedExpr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("unknown cast syntax")
|
||||
}
|
||||
|
||||
convertType, resolvedType, err := nodeResolvableTypeReference(ctx, node.Type, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we have the resolved type, then we've got a Doltgres type instead of a GMS type
|
||||
if !resolvedType.IsEmptyType() {
|
||||
cast, err := pgexprs.NewExplicitCastInjectable(resolvedType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: cast,
|
||||
Children: vitess.Exprs{expr},
|
||||
}, nil
|
||||
} else {
|
||||
convertType, err = translateConvertType(convertType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.ConvertExpr{
|
||||
Name: "CAST",
|
||||
Expr: expr,
|
||||
Type: convertType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
case *tree.CoalesceExpr:
|
||||
exprs, err := nodeExprsToSelectExprs(ctx, node.Exprs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vitess.FuncExpr{
|
||||
Name: vitess.NewColIdent("COALESCE"),
|
||||
Exprs: exprs,
|
||||
}, nil
|
||||
case *tree.CollateExpr:
|
||||
logrus.Warnf("collate is not yet supported, ignoring")
|
||||
return nodeExpr(ctx, node.Expr)
|
||||
case *tree.ColumnAccessExpr:
|
||||
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 {
|
||||
if node.TableName.NumParts > 2 {
|
||||
return nil, errors.Errorf("referencing items outside the database is not yet supported")
|
||||
}
|
||||
tableName.Name = vitess.NewTableIdent(node.TableName.Parts[0])
|
||||
tableName.SchemaQualifier = vitess.NewTableIdent(node.TableName.Parts[1])
|
||||
}
|
||||
return &vitess.ColName{
|
||||
Name: vitess.NewColIdent(string(node.ColumnName)),
|
||||
Qualifier: tableName,
|
||||
}, nil
|
||||
case *tree.CommentOnColumn:
|
||||
return nil, errors.Errorf("comment on column is not yet supported")
|
||||
case *tree.ComparisonExpr:
|
||||
// We will eventually support operators in other schemas, but for now we only can handle built-ins
|
||||
if len(node.Schema) > 0 && node.Schema != "pg_catalog" {
|
||||
return nil, errors.Errorf("schema %q not allowed in OPERATOR syntax", node.Schema)
|
||||
}
|
||||
|
||||
left, err := nodeExpr(ctx, node.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := nodeExpr(ctx, node.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var operator string
|
||||
switch node.Operator {
|
||||
case tree.EQ:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryEqual),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.LT:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessThan),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.GT:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterThan),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.LE:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.GE:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.NE:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryNotEqual),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.In, tree.NotIn:
|
||||
var innerExpression vitess.InjectedExpr
|
||||
switch right := right.(type) {
|
||||
case vitess.ValTuple:
|
||||
innerExpression = vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewInTuple(),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}
|
||||
case *vitess.Subquery:
|
||||
innerExpression = vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewInSubquery(),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}
|
||||
case vitess.InjectedExpr:
|
||||
if _, ok := right.Expression.(*pgexprs.RecordExpr); ok {
|
||||
innerExpression = vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewInTuple(),
|
||||
Children: vitess.Exprs{left, vitess.ValTuple(right.Children)},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if innerExpression.Expression == nil {
|
||||
return nil, errors.Errorf("right side of IN expression is not a tuple or subquery, got %T", right)
|
||||
}
|
||||
|
||||
switch node.Operator {
|
||||
case tree.In:
|
||||
return innerExpression, nil
|
||||
case tree.NotIn:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewNot(),
|
||||
Children: vitess.Exprs{innerExpression},
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown comparison operator used")
|
||||
}
|
||||
case tree.Like:
|
||||
operator = vitess.LikeStr
|
||||
case tree.NotLike:
|
||||
operator = vitess.NotLikeStr
|
||||
case tree.ILike:
|
||||
return nil, errors.Errorf("ILIKE is not yet supported")
|
||||
case tree.NotILike:
|
||||
return nil, errors.Errorf("ILIKE is not yet supported")
|
||||
case tree.SimilarTo:
|
||||
return nil, errors.Errorf("similar to is not yet supported")
|
||||
case tree.NotSimilarTo:
|
||||
return nil, errors.Errorf("not similar to is not yet supported")
|
||||
case tree.RegMatch:
|
||||
operator = vitess.RegexpStr
|
||||
case tree.NotRegMatch:
|
||||
operator = vitess.NotRegexpStr
|
||||
case tree.RegIMatch:
|
||||
return nil, errors.Errorf("~* is not yet supported")
|
||||
case tree.NotRegIMatch:
|
||||
return nil, errors.Errorf("!~* is not yet supported")
|
||||
case tree.TextSearchMatch:
|
||||
return nil, errors.Errorf("@@ is not yet supported")
|
||||
case tree.IsDistinctFrom:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewIsDistinctFrom(),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.IsNotDistinctFrom:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewIsNotDistinctFrom(),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.Contains:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONContainsRight),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.ContainedBy:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONContainsLeft),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.JSONExists:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevel),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.JSONSomeExists:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevelAny),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.JSONAllExists:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevelAll),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.Overlaps:
|
||||
return nil, errors.Errorf("&& is not yet supported")
|
||||
case tree.Any:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewAnyExpr(node.SubOperator.String()),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.Some:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewSomeExpr(node.SubOperator.String()),
|
||||
Children: vitess.Exprs{left, right},
|
||||
}, nil
|
||||
case tree.All:
|
||||
return nil, errors.Errorf("ALL is not yet supported")
|
||||
default:
|
||||
return nil, errors.Errorf("unknown comparison operator used")
|
||||
}
|
||||
return &vitess.ComparisonExpr{
|
||||
Operator: operator,
|
||||
Left: left,
|
||||
Right: right,
|
||||
Escape: nil, // TODO: is '\' the default in Postgres as well?
|
||||
}, nil
|
||||
case *tree.DArray:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DBitArray:
|
||||
// We convert bitarray to string representation for engine representation purposes so that we don't have to
|
||||
// represent another fundamental golang type. This means our representation in memory is more verbose.
|
||||
bitStr := tree.AsStringWithFlags(node, tree.FmtPgwireText)
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewUnsafeLiteral(bitStr, pgtypes.Bit),
|
||||
}, nil
|
||||
case *tree.DBool:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralBool(bool(*node)),
|
||||
}, nil
|
||||
case *tree.DBox2D:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DBytes:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DCollatedString:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DDate:
|
||||
t, err := node.Date.ToTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralDate(t),
|
||||
}, nil
|
||||
case *tree.DDecimal:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralNumeric(&node.Decimal)}, nil
|
||||
case *tree.DEnum:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DFloat:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralFloat64(float64(*node)),
|
||||
}, nil
|
||||
case *tree.DGeography:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DGeometry:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DIPAddr:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DInt:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralInt64(int64(*node)),
|
||||
}, nil
|
||||
case *tree.DInterval:
|
||||
cast, err := pgexprs.NewExplicitCastInjectable(pgtypes.Interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expr := pgexprs.NewIntervalLiteral(node.Duration)
|
||||
return vitess.InjectedExpr{
|
||||
Expression: cast,
|
||||
Children: vitess.Exprs{vitess.InjectedExpr{Expression: expr}},
|
||||
}, nil
|
||||
case *tree.DJSON:
|
||||
// JSON type is handled in string format
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralJSON(node.JSON.String()),
|
||||
}, nil
|
||||
case *tree.DOid:
|
||||
internalID := id.Cache().ToInternal(uint32(node.DInt))
|
||||
if !internalID.IsValid() {
|
||||
internalID = id.NewOID(uint32(node.DInt)).AsId()
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralOid(internalID),
|
||||
}, nil
|
||||
case *tree.DOidWrapper:
|
||||
return nodeExpr(ctx, node.Wrapped)
|
||||
case *tree.DString:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewUnknownLiteral(string(*node)),
|
||||
}, nil
|
||||
case *tree.DTime:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralTime(timeofday.TimeOfDay(*node)),
|
||||
}, nil
|
||||
case *tree.DTimeTZ:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralTimeTZ(node.TimeTZ),
|
||||
}, nil
|
||||
case *tree.DTimestamp:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralTimestamp(node.Time),
|
||||
}, nil
|
||||
case *tree.DTimestampTZ:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralTimestampTZ(node.Time),
|
||||
}, nil
|
||||
case *tree.DTuple:
|
||||
return nil, errors.Errorf("the statement is not yet supported")
|
||||
case *tree.DUuid:
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRawLiteralUuid(node.UUID),
|
||||
}, nil
|
||||
case tree.DefaultVal:
|
||||
// TODO: can we use this?
|
||||
defVal := &vitess.Default{ColName: ""}
|
||||
return defVal, nil
|
||||
case tree.DomainColumn:
|
||||
_, dataType, err := nodeResolvableTypeReference(ctx, node.Typ, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: &pgnodes.DomainColumn{Typ: dataType},
|
||||
}, nil
|
||||
case tree.FunctionColumn:
|
||||
if !node.FromCreate {
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewUnsafeLiteral(node.Val, node.Typ),
|
||||
}, nil
|
||||
} else {
|
||||
return vitess.InjectedExpr{
|
||||
Expression: &pgnodes.FunctionColumn{Name: node.Name, Typ: node.Typ, Idx: node.Idx},
|
||||
}, nil
|
||||
}
|
||||
case *tree.FuncExpr:
|
||||
return nodeFuncExpr(ctx, node)
|
||||
case *tree.IfErrExpr:
|
||||
return nil, errors.Errorf("IFERROR is not yet supported")
|
||||
case *tree.IfExpr:
|
||||
cond, err := nodeExpr(ctx, node.Cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trueVal, err := nodeExpr(ctx, node.True)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
falseVal, err := nodeExpr(ctx, node.Else)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: this could be a postgres func, but postgres doesn't have an IF function, this is an extension from cockroach
|
||||
return &vitess.FuncExpr{
|
||||
Name: vitess.NewColIdent("IF"),
|
||||
Exprs: vitess.SelectExprs{
|
||||
&vitess.AliasedExpr{
|
||||
Expr: cond,
|
||||
},
|
||||
&vitess.AliasedExpr{
|
||||
Expr: trueVal,
|
||||
},
|
||||
&vitess.AliasedExpr{
|
||||
Expr: falseVal,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case *tree.IndexedVar:
|
||||
// TODO: figure out if I can delete this
|
||||
return nil, errors.Errorf("this should probably be deleted (internal error, IndexedVar)")
|
||||
case *tree.IndirectionExpr:
|
||||
childExpr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(node.Indirection) > 1 {
|
||||
return nil, errors.Errorf("multi dimensional array subscripts are not yet supported")
|
||||
} else if node.Indirection[0].Slice {
|
||||
return nil, errors.Errorf("slice subscripts are not yet supported")
|
||||
}
|
||||
|
||||
indexExpr, err := nodeExpr(ctx, node.Indirection[0].Begin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vitess.InjectedExpr{
|
||||
Expression: &pgexprs.Subscript{},
|
||||
Children: vitess.Exprs{childExpr, indexExpr},
|
||||
}, nil
|
||||
case *tree.IsNotNullExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.IsExpr{
|
||||
Operator: vitess.IsNotNullStr,
|
||||
Expr: expr,
|
||||
}, nil
|
||||
case *tree.IsNullExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.IsExpr{
|
||||
Operator: vitess.IsNullStr,
|
||||
Expr: expr,
|
||||
}, nil
|
||||
case *tree.IsOfTypeExpr:
|
||||
return nil, errors.Errorf("IS OF is not yet supported")
|
||||
case *tree.NotExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.NotExpr{
|
||||
Expr: expr,
|
||||
}, nil
|
||||
case *tree.NullIfExpr:
|
||||
expr1, err := nodeExprToSelectExpr(ctx, node.Expr1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expr2, err := nodeExprToSelectExpr(ctx, node.Expr2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vitess.FuncExpr{
|
||||
Name: vitess.NewColIdent("NULLIF"),
|
||||
Exprs: vitess.SelectExprs{expr1, expr2},
|
||||
}, nil
|
||||
case tree.NullLiteral:
|
||||
return &vitess.NullVal{}, nil
|
||||
case *tree.NumVal:
|
||||
switch node.Kind() {
|
||||
case constant.Int:
|
||||
intLiteral, err := pgexprs.NewIntegerLiteral(node.FormattedString())
|
||||
return vitess.InjectedExpr{
|
||||
Expression: intLiteral,
|
||||
}, err
|
||||
case constant.Float:
|
||||
numericLiteral, err := pgexprs.NewNumericLiteral(node.FormattedString())
|
||||
return vitess.InjectedExpr{
|
||||
Expression: numericLiteral,
|
||||
}, err
|
||||
default:
|
||||
return nil, errors.Errorf("unknown number format")
|
||||
}
|
||||
case *tree.OrExpr:
|
||||
left, err := nodeExpr(ctx, node.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := nodeExpr(ctx, node.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.OrExpr{
|
||||
Left: left,
|
||||
Right: right,
|
||||
}, nil
|
||||
case *tree.ParenExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.ParenExpr{
|
||||
Expr: expr,
|
||||
}, nil
|
||||
case *tree.PartitionMaxVal:
|
||||
return nil, errors.Errorf("MAXVALUE is not yet supported")
|
||||
case *tree.PartitionMinVal:
|
||||
return nil, errors.Errorf("MINVALUE is not yet supported")
|
||||
case *tree.Placeholder:
|
||||
// TODO: deal with type annotation
|
||||
mysqlBindVarIdx := node.Idx + 1
|
||||
return vitess.NewValArg([]byte(fmt.Sprintf(":v%d", mysqlBindVarIdx))), nil
|
||||
case *tree.RangeCond:
|
||||
left, err := nodeExpr(ctx, node.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
from, err := nodeExpr(ctx, node.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
to, err := nodeExpr(ctx, node.To)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retExpr := vitess.Expr(&vitess.AndExpr{
|
||||
Left: vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
||||
Children: vitess.Exprs{left, from},
|
||||
},
|
||||
Right: vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
||||
Children: vitess.Exprs{left, to},
|
||||
},
|
||||
})
|
||||
if node.Symmetric {
|
||||
retExpr = &vitess.OrExpr{
|
||||
Left: retExpr,
|
||||
Right: &vitess.AndExpr{
|
||||
Left: vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
||||
Children: vitess.Exprs{left, to},
|
||||
},
|
||||
Right: vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
||||
Children: vitess.Exprs{left, from},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if node.Not {
|
||||
retExpr = vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewNot(),
|
||||
Children: vitess.Exprs{retExpr},
|
||||
}
|
||||
}
|
||||
return retExpr, nil
|
||||
case *tree.StrVal:
|
||||
// TODO: determine what to do when node.WasScannedAsBytes() is true
|
||||
// For string literals, we mark the type as unknown, because Postgres has
|
||||
// more permissive implicit casting rules for literals than it does for strongly
|
||||
// typed values from a schema for example.
|
||||
unknownLiteral := pgexprs.NewUnknownLiteral(node.RawString())
|
||||
return vitess.InjectedExpr{
|
||||
Expression: unknownLiteral,
|
||||
}, nil
|
||||
case *tree.Subquery:
|
||||
return nodeSubqueryOrExists(ctx, node)
|
||||
case *tree.Tuple:
|
||||
if len(node.Labels) > 0 {
|
||||
return nil, errors.Errorf("tuple labels are not yet supported")
|
||||
}
|
||||
|
||||
valTuple, err := nodeExprs(ctx, node.Exprs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewRecordExpr(),
|
||||
Children: valTuple,
|
||||
}, nil
|
||||
case *tree.TupleStar:
|
||||
return nil, errors.Errorf("(E).* is not yet supported")
|
||||
case *tree.UnaryExpr:
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var operator framework.Operator
|
||||
switch node.Operator {
|
||||
// TODO: need to add UnaryPlus, it's like a no-op but Postgres actually implements it and it affects coercion
|
||||
case tree.UnaryMinus:
|
||||
operator = framework.Operator_UnaryMinus
|
||||
case tree.UnaryComplement:
|
||||
return &vitess.UnaryExpr{
|
||||
Operator: vitess.TildaStr,
|
||||
Expr: expr,
|
||||
}, nil
|
||||
case tree.UnarySqrt:
|
||||
// TODO: replace with a function
|
||||
return nil, errors.Errorf("square root operator is not yet supported")
|
||||
case tree.UnaryCbrt:
|
||||
// TODO: replace with a function
|
||||
return nil, errors.Errorf("cube root operator is not yet supported")
|
||||
case tree.UnaryAbsolute:
|
||||
// TODO: replace with a function
|
||||
return nil, errors.Errorf("absolute operator is not yet supported")
|
||||
default:
|
||||
return nil, errors.Errorf("the unary operator used is not yet supported")
|
||||
}
|
||||
return vitess.InjectedExpr{
|
||||
Expression: pgexprs.NewUnaryOperator(operator),
|
||||
Children: vitess.Exprs{expr},
|
||||
}, nil
|
||||
case tree.UnqualifiedStar:
|
||||
return nil, errors.Errorf("* syntax is not yet supported in this context")
|
||||
case *tree.UnresolvedName:
|
||||
if node.Star {
|
||||
return nil, errors.Errorf("* syntax is not yet supported in this context")
|
||||
}
|
||||
return unresolvedNameToColName(node)
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown expression: `%T`", node)
|
||||
}
|
||||
}
|
||||
|
||||
// unresolvedNameToColName converts a tree.UnresolvedName to a vitess.ColName with the appropriate name qualifiers set.
|
||||
func unresolvedNameToColName(name *tree.UnresolvedName) (*vitess.ColName, error) {
|
||||
var tableName vitess.TableName
|
||||
switch name.NumParts {
|
||||
case 4:
|
||||
tableName = vitess.TableName{
|
||||
Name: vitess.NewTableIdent(name.Parts[1]),
|
||||
SchemaQualifier: vitess.NewTableIdent(name.Parts[2]),
|
||||
DbQualifier: vitess.NewTableIdent(name.Parts[3]),
|
||||
}
|
||||
case 3:
|
||||
tableName = vitess.TableName{
|
||||
Name: vitess.NewTableIdent(name.Parts[1]),
|
||||
SchemaQualifier: vitess.NewTableIdent(name.Parts[2]),
|
||||
}
|
||||
case 2:
|
||||
tableName = vitess.TableName{
|
||||
Name: vitess.NewTableIdent(name.Parts[1]),
|
||||
}
|
||||
case 1:
|
||||
// no table name
|
||||
default:
|
||||
return nil, errors.Errorf("invalid name: %s", name)
|
||||
}
|
||||
|
||||
return &vitess.ColName{
|
||||
Name: vitess.NewColIdent(name.Parts[0]),
|
||||
Qualifier: tableName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// translateConvertType translates the *vitess.ConvertType expression given to a new one, substituting type names as
|
||||
// appropriate. An error is returned if the type named cannot be supported.
|
||||
func translateConvertType(convertType *vitess.ConvertType) (*vitess.ConvertType, error) {
|
||||
switch strings.ToLower(convertType.Type) {
|
||||
// passthrough types that need no conversion
|
||||
case expression.ConvertToBinary, expression.ConvertToChar, expression.ConvertToNChar, expression.ConvertToDate,
|
||||
expression.ConvertToDatetime, expression.ConvertToFloat, expression.ConvertToDouble, expression.ConvertToJSON,
|
||||
expression.ConvertToReal, expression.ConvertToSigned, expression.ConvertToTime, expression.ConvertToUnsigned:
|
||||
return convertType, nil
|
||||
case "text", "character varying", "varchar":
|
||||
return &vitess.ConvertType{
|
||||
Type: expression.ConvertToChar,
|
||||
}, nil
|
||||
case "integer", "bigint":
|
||||
return &vitess.ConvertType{
|
||||
Type: expression.ConvertToSigned,
|
||||
}, nil
|
||||
case "decimal", "numeric":
|
||||
return &vitess.ConvertType{
|
||||
Type: expression.ConvertToFloat,
|
||||
}, nil
|
||||
case "boolean":
|
||||
return &vitess.ConvertType{
|
||||
Type: expression.ConvertToSigned,
|
||||
}, nil
|
||||
case "timestamp", "timestamp with time zone", "timestamp without time zone":
|
||||
return &vitess.ConvertType{
|
||||
Type: expression.ConvertToDatetime,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown convert type: `%T`", convertType.Type)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeForeignKeyConstraintTableDef handles *tree.ForeignKeyConstraintTableDef nodes.
|
||||
func nodeForeignKeyConstraintTableDef(ctx *Context, node *tree.ForeignKeyConstraintTableDef, notValid bool) (*vitess.ForeignKeyDefinition, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var matchType vitess.ForeignKeyMatchType
|
||||
switch node.Match {
|
||||
case tree.MatchSimple:
|
||||
matchType = vitess.MatchSimple
|
||||
case tree.MatchFull:
|
||||
matchType = vitess.MatchFull
|
||||
case tree.MatchPartial:
|
||||
return nil, errors.Errorf("MATCH PARTIAL is not yet supported")
|
||||
default:
|
||||
return nil, errors.Errorf("unknown foreign key MATCH strategy")
|
||||
}
|
||||
tableName, err := nodeTableName(ctx, &node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fromCols := make([]vitess.ColIdent, len(node.FromCols))
|
||||
for i := range node.FromCols {
|
||||
fromCols[i] = vitess.NewColIdent(string(node.FromCols[i]))
|
||||
}
|
||||
toCols := make([]vitess.ColIdent, len(node.ToCols))
|
||||
for i := range node.ToCols {
|
||||
toCols[i] = vitess.NewColIdent(string(node.ToCols[i]))
|
||||
}
|
||||
var refActions [2]vitess.ReferenceAction
|
||||
for i, refAction := range []tree.RefAction{node.Actions.Delete, node.Actions.Update} {
|
||||
switch refAction.Action {
|
||||
case tree.NoAction:
|
||||
refActions[i] = vitess.NoAction
|
||||
case tree.Restrict:
|
||||
refActions[i] = vitess.Restrict
|
||||
case tree.SetNull:
|
||||
refActions[i] = vitess.SetNull
|
||||
if refAction.Columns != nil {
|
||||
return nil, errors.Errorf("SET NULL <columns> is not yet supported")
|
||||
}
|
||||
case tree.SetDefault:
|
||||
refActions[i] = vitess.SetDefault
|
||||
if refAction.Columns != nil {
|
||||
return nil, errors.Errorf("SET DEFAULT <columns> is not yet supported")
|
||||
}
|
||||
case tree.Cascade:
|
||||
refActions[i] = vitess.Cascade
|
||||
default:
|
||||
return nil, errors.Errorf("unknown foreign key reference action encountered")
|
||||
}
|
||||
}
|
||||
return &vitess.ForeignKeyDefinition{
|
||||
Source: fromCols,
|
||||
ReferencedTable: tableName,
|
||||
ReferencedColumns: toCols,
|
||||
OnDelete: refActions[0],
|
||||
OnUpdate: refActions[1],
|
||||
NotValid: notValid,
|
||||
MatchType: matchType,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeFrom handles *tree.From nodes.
|
||||
func nodeFrom(ctx *Context, node tree.From) (vitess.TableExprs, error) {
|
||||
if len(node.Tables) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
tableExprs, err := nodeTableExprs(ctx, node.Tables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tableExprs, err
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
||||
)
|
||||
|
||||
// nodeFuncExpr handles *tree.FuncExpr nodes.
|
||||
func nodeFuncExpr(ctx *Context, node *tree.FuncExpr) (vitess.Expr, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.Filter != nil {
|
||||
return nil, errors.Errorf("function filters are not yet supported")
|
||||
}
|
||||
if node.AggType == tree.OrderedSetAgg {
|
||||
return nil, errors.Errorf("WITHIN GROUP is not yet supported")
|
||||
}
|
||||
|
||||
var qualifier vitess.TableIdent
|
||||
var name vitess.ColIdent
|
||||
switch funcRef := node.Func.FunctionReference.(type) {
|
||||
case *tree.FunctionDefinition:
|
||||
name = vitess.NewColIdent(funcRef.Name)
|
||||
case *tree.UnresolvedName:
|
||||
colName, err := unresolvedNameToColName(funcRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qualifier = colName.Qualifier.Name
|
||||
name = colName.Name
|
||||
default:
|
||||
return nil, errors.Errorf("unknown function reference")
|
||||
}
|
||||
var distinct bool
|
||||
switch node.Type {
|
||||
case 0, tree.AllFuncType:
|
||||
distinct = false
|
||||
case tree.DistinctFuncType:
|
||||
distinct = true
|
||||
default:
|
||||
return nil, errors.Errorf("unknown function spec type %d", node.Type)
|
||||
}
|
||||
windowDef, err := nodeWindowDef(ctx, node.WindowDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exprs, err := nodeExprsToSelectExprs(ctx, node.Exprs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch strings.ToLower(name.String()) {
|
||||
// special case for string_agg, which maps to the mysql aggregate function group_concat
|
||||
case "string_agg":
|
||||
if len(node.Exprs) != 2 {
|
||||
return nil, errors.Errorf("string_agg requires two arguments")
|
||||
}
|
||||
|
||||
sepString := ""
|
||||
if sep, ok := node.Exprs[1].(*tree.StrVal); ok {
|
||||
sepString = strings.Trim(sep.String(), "'")
|
||||
} else {
|
||||
// TODO: need to support this function in doltgres
|
||||
c, is := node.Exprs[1].(*tree.CastExpr)
|
||||
if !is && c.Type.SQLString() != "TEXT" {
|
||||
return nil, errors.Errorf("string_agg requires a string separator")
|
||||
}
|
||||
sepString = strings.Trim(c.Expr.String(), "'")
|
||||
}
|
||||
|
||||
var orderBy vitess.OrderBy
|
||||
if len(node.OrderBy) > 0 {
|
||||
orderBy, err = nodeOrderBy(ctx, node.OrderBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &vitess.GroupConcatExpr{
|
||||
Exprs: exprs[:1],
|
||||
Separator: vitess.Separator{
|
||||
SeparatorString: sepString,
|
||||
},
|
||||
OrderBy: orderBy,
|
||||
}, nil
|
||||
case "array_agg":
|
||||
var orderBy vitess.OrderBy
|
||||
if len(node.OrderBy) > 0 {
|
||||
orderBy, err = nodeOrderBy(ctx, node.OrderBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &vitess.OrderedInjectedExpr{
|
||||
InjectedExpr: vitess.InjectedExpr{
|
||||
Expression: &pgexprs.ArrayAgg{Distinct: distinct},
|
||||
SelectExprChildren: exprs,
|
||||
Auth: vitess.AuthInformation{},
|
||||
},
|
||||
OrderBy: orderBy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(node.OrderBy) > 0 {
|
||||
return nil, errors.Errorf("function ORDER BY is not yet supported")
|
||||
}
|
||||
|
||||
return &vitess.FuncExpr{
|
||||
Qualifier: qualifier,
|
||||
Name: name,
|
||||
Distinct: distinct,
|
||||
Exprs: exprs,
|
||||
Over: (*vitess.Over)(windowDef),
|
||||
Auth: vitess.AuthInformation{
|
||||
AuthType: auth.AuthType_EXECUTE,
|
||||
TargetType: auth.AuthTargetType_FunctionIdentifiers,
|
||||
TargetNames: []string{qualifier.String(), name.String()},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/privilege"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeGrant handles *tree.Grant nodes.
|
||||
func nodeGrant(ctx *Context, node *tree.Grant) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var grantTable *pgnodes.GrantTable
|
||||
var grantSchema *pgnodes.GrantSchema
|
||||
var grantDatabase *pgnodes.GrantDatabase
|
||||
var grantSequence *pgnodes.GrantSequence
|
||||
var grantRoutine *pgnodes.GrantRoutine
|
||||
switch node.Targets.TargetType {
|
||||
case privilege.Table:
|
||||
tables := make([]doltdb.TableName, 0, len(node.Targets.Tables)+len(node.Targets.InSchema))
|
||||
for _, table := range node.Targets.Tables {
|
||||
normalizedTable, err := table.NormalizeTablePattern()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch normalizedTable := normalizedTable.(type) {
|
||||
case *tree.TableName:
|
||||
if normalizedTable.ExplicitCatalog {
|
||||
return nil, errors.Errorf("granting privileges to other databases is not yet supported")
|
||||
}
|
||||
tables = append(tables, doltdb.TableName{
|
||||
Name: string(normalizedTable.ObjectName),
|
||||
Schema: string(normalizedTable.SchemaName),
|
||||
})
|
||||
case *tree.AllTablesSelector:
|
||||
tables = append(tables, doltdb.TableName{
|
||||
Name: "",
|
||||
Schema: string(normalizedTable.SchemaName),
|
||||
})
|
||||
default:
|
||||
return nil, errors.Errorf(`unexpected table type in GRANT: %T`, normalizedTable)
|
||||
}
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
tables = append(tables, doltdb.TableName{
|
||||
Name: "",
|
||||
Schema: schema,
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_TABLE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantTable = &pgnodes.GrantTable{
|
||||
Privileges: privileges,
|
||||
Tables: tables,
|
||||
}
|
||||
case privilege.Schema:
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SCHEMA, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantSchema = &pgnodes.GrantSchema{
|
||||
Privileges: privileges,
|
||||
Schemas: node.Targets.Names,
|
||||
}
|
||||
case privilege.Database:
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_DATABASE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantDatabase = &pgnodes.GrantDatabase{
|
||||
Privileges: privileges,
|
||||
Databases: node.Targets.Databases.ToStrings(),
|
||||
}
|
||||
case privilege.Sequence:
|
||||
sequences := make([]auth.SequencePrivilegeKey, 0, len(node.Targets.Sequences)+len(node.Targets.InSchema))
|
||||
for _, seq := range node.Targets.Sequences {
|
||||
sequences = append(sequences, auth.SequencePrivilegeKey{
|
||||
Schema: sequenceSchema(seq),
|
||||
Name: seq.Parts[0],
|
||||
})
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
sequences = append(sequences, auth.SequencePrivilegeKey{
|
||||
Schema: schema,
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SEQUENCE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantSequence = &pgnodes.GrantSequence{
|
||||
Privileges: privileges,
|
||||
Sequences: sequences,
|
||||
}
|
||||
case privilege.Function, privilege.Procedure, privilege.Routine:
|
||||
routines := make([]auth.RoutinePrivilegeKey, 0, len(node.Targets.Routines)+len(node.Targets.InSchema))
|
||||
for _, r := range node.Targets.Routines {
|
||||
routines = append(routines, auth.RoutinePrivilegeKey{
|
||||
Schema: routineSchema(r.Name),
|
||||
Name: r.Name.Parts[0],
|
||||
// TODO: there can be 2 routines with the same name but different argument types
|
||||
// need a fix for getting argument types from parsing CALL statement
|
||||
//ArgTypes: routineArgTypesKey(r.Args),
|
||||
})
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
routines = append(routines, auth.RoutinePrivilegeKey{
|
||||
Schema: schema,
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_FUNCTION, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grantRoutine = &pgnodes.GrantRoutine{
|
||||
Privileges: privileges,
|
||||
Routines: routines,
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("this form of GRANT is not yet supported")
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.Grant{
|
||||
GrantTable: grantTable,
|
||||
GrantSchema: grantSchema,
|
||||
GrantDatabase: grantDatabase,
|
||||
GrantSequence: grantSequence,
|
||||
GrantRoutine: grantRoutine,
|
||||
GrantRole: nil,
|
||||
ToRoles: node.Grantees,
|
||||
WithGrantOption: node.WithGrantOption,
|
||||
GrantedBy: node.GrantedBy,
|
||||
},
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// sequenceSchema returns the schema portion of an UnresolvedObjectName for a sequence.
|
||||
func sequenceSchema(name *tree.UnresolvedObjectName) string {
|
||||
if name.NumParts >= 2 {
|
||||
return name.Parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// routineSchema returns the schema portion of an UnresolvedObjectName for a routine.
|
||||
func routineSchema(name *tree.UnresolvedObjectName) string {
|
||||
if name.NumParts >= 2 {
|
||||
return name.Parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// routineArgTypesKey builds a canonical string key from a RoutineArgs list using only the argument types.
|
||||
func routineArgTypesKey(args tree.RoutineArgs) string {
|
||||
parts := make([]string, len(args))
|
||||
for i, arg := range args {
|
||||
parts[i] = arg.Type.SQLString()
|
||||
}
|
||||
return strings.Join(parts, ",")
|
||||
}
|
||||
|
||||
// convertPrivilegeKind converts a privilege from its parser representation to the server representation.
|
||||
func convertPrivilegeKinds(object auth.PrivilegeObject, kinds []privilege.Kind) ([]auth.Privilege, error) {
|
||||
privileges := make([]auth.Privilege, len(kinds))
|
||||
for i, kind := range kinds {
|
||||
switch kind {
|
||||
case privilege.ALL:
|
||||
// If we encounter ALL, then we know to return all privileges for this object
|
||||
return object.AllPrivileges(), nil
|
||||
case privilege.ALTERSYSTEM:
|
||||
privileges[i] = auth.Privilege_ALTER_SYSTEM
|
||||
case privilege.CONNECT:
|
||||
privileges[i] = auth.Privilege_CONNECT
|
||||
case privilege.CREATE:
|
||||
privileges[i] = auth.Privilege_CREATE
|
||||
case privilege.DELETE:
|
||||
privileges[i] = auth.Privilege_DELETE
|
||||
case privilege.DROP:
|
||||
privileges[i] = auth.Privilege_DROP
|
||||
case privilege.EXECUTE:
|
||||
privileges[i] = auth.Privilege_EXECUTE
|
||||
case privilege.INSERT:
|
||||
privileges[i] = auth.Privilege_INSERT
|
||||
case privilege.REFERENCES:
|
||||
privileges[i] = auth.Privilege_REFERENCES
|
||||
case privilege.SELECT:
|
||||
privileges[i] = auth.Privilege_SELECT
|
||||
case privilege.SET:
|
||||
privileges[i] = auth.Privilege_SET
|
||||
case privilege.TEMPORARY:
|
||||
privileges[i] = auth.Privilege_TEMPORARY
|
||||
case privilege.TRIGGER:
|
||||
privileges[i] = auth.Privilege_TRIGGER
|
||||
case privilege.TRUNCATE:
|
||||
privileges[i] = auth.Privilege_TRUNCATE
|
||||
case privilege.UPDATE:
|
||||
privileges[i] = auth.Privilege_UPDATE
|
||||
case privilege.USAGE:
|
||||
privileges[i] = auth.Privilege_USAGE
|
||||
default:
|
||||
// This shouldn't be possible unless we update our list of supported privileges
|
||||
return nil, errors.Errorf("unknown privilege kind: %v", kind)
|
||||
}
|
||||
}
|
||||
for _, p := range privileges {
|
||||
if !object.IsValid(p) {
|
||||
return nil, errors.Errorf("invalid privilege type %s for relation", p.String())
|
||||
}
|
||||
}
|
||||
return privileges, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 (
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeGrantRole handles *tree.GrantRole nodes.
|
||||
func nodeGrantRole(ctx *Context, node *tree.GrantRole) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.Grant{
|
||||
GrantRole: &pgnodes.GrantRole{
|
||||
Groups: node.Roles.ToStrings(),
|
||||
},
|
||||
ToRoles: node.Members,
|
||||
WithGrantOption: len(node.WithOption) > 0,
|
||||
GrantedBy: node.GrantedBy,
|
||||
},
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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/dolthub/go-mysql-server/sql/expression"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeGroupBy handles tree.GroupBy nodes.
|
||||
func nodeGroupBy(ctx *Context, node tree.GroupBy) (vitess.GroupBy, error) {
|
||||
if len(node) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
groupBys := make(vitess.GroupBy, len(node))
|
||||
var err error
|
||||
for i, expr := range node {
|
||||
groupBys[i], err = nodeExpr(ctx, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GMS order by is hardcoded to expect vitess.SQLVal for expressions such as `ORDER BY 1`.
|
||||
// In addition, there is the requirement that columns in the order by also need to be referenced somewhere in
|
||||
// the query, which is not a requirement for Postgres. Whenever we add that functionality, we also need to
|
||||
// remove the dependency on vitess.SQLVal. For now, we'll just convert our literals to a vitess.SQLVal.
|
||||
if injectedExpr, ok := groupBys[i].(vitess.InjectedExpr); ok {
|
||||
if literal, ok := injectedExpr.Expression.(*expression.Literal); ok {
|
||||
groupBys[i] = pgexprs.ToVitessLiteral(literal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groupBys, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeImport handles *tree.Import nodes.
|
||||
func nodeImport(ctx *Context, node *tree.Import) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("IMPORT is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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/sirupsen/logrus"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeIndexElemList converts a tree.IndexElemList to a slice of vitess.IndexField.
|
||||
func nodeIndexElemList(ctx *Context, node tree.IndexElemList) ([]*vitess.IndexField, error) {
|
||||
vitessIndexColumns := make([]*vitess.IndexField, 0, len(node))
|
||||
for _, inputColumn := range node {
|
||||
if inputColumn.Collation != "" {
|
||||
logrus.Warn("index attribute collation is not yet supported, ignoring")
|
||||
}
|
||||
if inputColumn.OpClass != nil {
|
||||
logrus.Warn("index attribute operator class is not yet supported, ignoring")
|
||||
}
|
||||
if inputColumn.ExcludeOp != nil {
|
||||
return nil, errors.Errorf("index attribute exclude operator is not yet supported")
|
||||
}
|
||||
|
||||
switch inputColumn.Direction {
|
||||
case tree.DefaultDirection:
|
||||
// Defaults to ASC
|
||||
case tree.Ascending:
|
||||
// The only default supported in GMS for now
|
||||
case tree.Descending:
|
||||
logrus.Warn("descending indexes are not yet supported, ignoring sort order")
|
||||
default:
|
||||
return nil, errors.Errorf("unknown index sorting direction encountered")
|
||||
}
|
||||
|
||||
switch inputColumn.NullsOrder {
|
||||
case tree.DefaultNullsOrder:
|
||||
// TODO: the default NULL order is reversed compared to MySQL, so the default is technically always wrong.
|
||||
// To prevent choking on every index, we allow this to proceed (even with incorrect results) for now.
|
||||
case tree.NullsFirst:
|
||||
// The only form supported in GMS for now
|
||||
case tree.NullsLast:
|
||||
return nil, errors.Errorf("NULLS LAST for indexes is not yet supported")
|
||||
default:
|
||||
return nil, errors.Errorf("unknown NULL ordering for index")
|
||||
}
|
||||
|
||||
var expr vitess.Expr
|
||||
if inputColumn.Expr != nil {
|
||||
var err error
|
||||
expr, err = nodeExpr(ctx, inputColumn.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
vitessIndexColumns = append(vitessIndexColumns, &vitess.IndexField{
|
||||
Column: vitess.NewColIdent(string(inputColumn.Column)),
|
||||
Order: vitess.AscScr,
|
||||
Expression: expr,
|
||||
})
|
||||
}
|
||||
|
||||
return vitessIndexColumns, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeIndexTableDef handles *tree.IndexTableDef nodes. The parser does not store type information in the index
|
||||
// definition (PRIMARY KEY, UNIQUE, etc.) so it must be added to this definition by the caller.
|
||||
func nodeIndexTableDef(ctx *Context, node *tree.IndexTableDef) (*vitess.IndexDefinition, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.IndexParams.IncludeColumns != nil {
|
||||
return nil, errors.Errorf("include columns is not yet supported")
|
||||
}
|
||||
if len(node.IndexParams.StorageParams) > 0 {
|
||||
logrus.Warn("storage params are not yet supported, ignoring")
|
||||
}
|
||||
if node.IndexParams.Tablespace != "" {
|
||||
logrus.Warn("tablespace is not yet supported, ignoring")
|
||||
}
|
||||
|
||||
indexFields, err := nodeIndexElemList(ctx, node.Columns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vitess.IndexDefinition{
|
||||
Info: &vitess.IndexInfo{
|
||||
Type: "",
|
||||
Name: vitess.NewColIdent(string(node.Name)),
|
||||
},
|
||||
Fields: indexFields,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
||||
)
|
||||
|
||||
// nodeLimit handles *tree.Limit nodes.
|
||||
func nodeLimit(ctx *Context, node *tree.Limit) (*vitess.Limit, error) {
|
||||
if node == nil || ((node.Count == nil || node.Count == tree.NullLiteral{}) && (node.Offset == nil || node.Offset == tree.NullLiteral{})) {
|
||||
return nil, nil
|
||||
}
|
||||
var count vitess.Expr
|
||||
if !node.LimitAll {
|
||||
var err error
|
||||
count, err = nodeExpr(ctx, node.Count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
offset, err := nodeExpr(ctx, node.Offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// GMS is hardcoded to expect vitess.SQLVal for expressions such as `LIMIT 1 OFFSET 1`.
|
||||
// We need to remove the hard dependency, but for now, we'll just convert our literals to a vitess.SQLVal.
|
||||
if injectedExpr, ok := count.(vitess.InjectedExpr); ok {
|
||||
if literal, ok := injectedExpr.Expression.(*expression.Literal); ok {
|
||||
l := literal.Value()
|
||||
limitValue, err := int64ValueForLimit(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if limitValue < 0 {
|
||||
return nil, errors.Errorf("LIMIT must be greater than or equal to 0")
|
||||
}
|
||||
|
||||
count = pgexprs.ToVitessLiteral(literal)
|
||||
}
|
||||
}
|
||||
if injectedExpr, ok := offset.(vitess.InjectedExpr); ok {
|
||||
if literal, ok := injectedExpr.Expression.(*expression.Literal); ok {
|
||||
o := literal.Value()
|
||||
offsetVal, err := int64ValueForLimit(o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if offsetVal < 0 {
|
||||
return nil, errors.Errorf("OFFSET must be greater than or equal to 0")
|
||||
}
|
||||
|
||||
offset = pgexprs.ToVitessLiteral(literal)
|
||||
}
|
||||
}
|
||||
return &vitess.Limit{
|
||||
Offset: offset,
|
||||
Rowcount: count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// int64ValueForLimit converts a literal value to an int64
|
||||
func int64ValueForLimit(l any) (int64, error) {
|
||||
var limitValue int64
|
||||
switch l := l.(type) {
|
||||
case int:
|
||||
limitValue = int64(l)
|
||||
case int32:
|
||||
limitValue = int64(l)
|
||||
case int64:
|
||||
limitValue = l
|
||||
case float64:
|
||||
limitValue = int64(l)
|
||||
case float32:
|
||||
limitValue = int64(l)
|
||||
default:
|
||||
return 0, errors.Errorf("unsupported limit/offset value type %T", l)
|
||||
}
|
||||
return limitValue, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeLockingClause handles *tree.LockingClause nodes.
|
||||
func nodeLockingClause(ctx *Context, node tree.LockingClause) (vitess.Statement, error) {
|
||||
if len(node) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Errorf("locking clauses are not yet supported")
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
// Copyright 2025 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 (
|
||||
"os"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
const ignoreUnsupportedEnvKey = "DOLTGRES_IGNORE_UNSUPPORTED"
|
||||
|
||||
// ignoreUnsupportedStatements is a flag that determines whether to ignore unsupported statements. This is useful
|
||||
// when importing a dump from postgres using certain import tools that expect every statement to succeed, including
|
||||
// ones that we can't yet fully support (or that we never will, but are safe to ignore).
|
||||
var ignoreUnsupportedStatements bool
|
||||
|
||||
func init() {
|
||||
if _, ignoreUnsupported := os.LookupEnv(ignoreUnsupportedEnvKey); ignoreUnsupported {
|
||||
ignoreUnsupportedStatements = true
|
||||
}
|
||||
}
|
||||
|
||||
// NewNoOp returns a new NoOp statement which does nothing and issues zero or more warnings when run.
|
||||
// Used for statements that aren't directly supported but which we don't want to cause errors.
|
||||
func NewNoOp(warnings ...string) vitess.InjectedStatement {
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NoOp{
|
||||
Warnings: warnings,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NotYetSupportedError returns an unsupported error with the given message, or a NoOp statement if the environment
|
||||
// variable DOLTGRES_IGNORE_UNSUPPORTED is set.
|
||||
func NotYetSupportedError(errorMsg string) (vitess.Statement, error) {
|
||||
if ignoreUnsupportedStatements {
|
||||
return NewNoOp(errorMsg), nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf(errorMsg + " Please file an issue at https://github.com/dolthub/doltgresql/issues")
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
||||
)
|
||||
|
||||
// nodeOrderBy handles *tree.OrderBy nodes.
|
||||
func nodeOrderBy(ctx *Context, node tree.OrderBy) (vitess.OrderBy, error) {
|
||||
if len(node) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
orderBys := make([]*vitess.Order, len(node))
|
||||
for i := range node {
|
||||
if node[i].OrderType != tree.OrderByColumn {
|
||||
return nil, errors.Errorf("ORDER BY type is not yet supported")
|
||||
}
|
||||
var direction string
|
||||
switch node[i].Direction {
|
||||
case tree.DefaultDirection:
|
||||
direction = vitess.AscScr
|
||||
case tree.Ascending:
|
||||
direction = vitess.AscScr
|
||||
case tree.Descending:
|
||||
direction = vitess.DescScr
|
||||
default:
|
||||
return nil, errors.Errorf("unknown ORDER BY sorting direction")
|
||||
}
|
||||
switch node[i].NullsOrder {
|
||||
case tree.DefaultNullsOrder:
|
||||
//TODO: the default NULL order is reversed compared to MySQL, so the default is technically always wrong.
|
||||
// To prevent choking on every ORDER BY, we allow this to proceed (even with incorrect results) for now.
|
||||
// If the NULL order is explicitly declared, then we want to error rather than return incorrect results.
|
||||
case tree.NullsFirst:
|
||||
if direction != vitess.AscScr {
|
||||
return nil, errors.Errorf("this NULL ordering is not yet supported for this ORDER BY direction")
|
||||
}
|
||||
case tree.NullsLast:
|
||||
if direction != vitess.DescScr {
|
||||
return nil, errors.Errorf("this NULL ordering is not yet supported for this ORDER BY direction")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("unknown NULL ordering in ORDER BY")
|
||||
}
|
||||
expr, err := nodeExpr(ctx, node[i].Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// GMS order by is hardcoded to expect vitess.SQLVal for expressions such as `ORDER BY 1`.
|
||||
// In addition, there is the requirement that columns in the order by also need to be referenced somewhere in
|
||||
// the query, which is not a requirement for Postgres. Whenever we add that functionality, we also need to
|
||||
// remove the dependency on vitess.SQLVal. For now, we'll just convert our literals to a vitess.SQLVal.
|
||||
if injectedExpr, ok := expr.(vitess.InjectedExpr); ok {
|
||||
if literal, ok := injectedExpr.Expression.(*expression.Literal); ok {
|
||||
expr = pgexprs.ToVitessLiteral(literal)
|
||||
}
|
||||
}
|
||||
orderBys[i] = &vitess.Order{
|
||||
Expr: expr,
|
||||
Direction: direction,
|
||||
}
|
||||
}
|
||||
return orderBys, nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeParenSelect handles tree.ParenSelect nodes.
|
||||
func nodeParenSelect(ctx *Context, node *tree.ParenSelect) (*vitess.ParenSelect, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
select_, err := nodeSelect(ctx, node.Select)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.ParenSelect{
|
||||
Select: select_,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodePrepare handles *tree.Prepare nodes.
|
||||
func nodePrepare(ctx *Context, node *tree.Prepare) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("PREPARE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRefreshMaterializedView handles *tree.RefreshMaterializedView nodes.
|
||||
func nodeRefreshMaterializedView(ctx *Context, node *tree.RefreshMaterializedView) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("REFRESH MATERIALIZED VIEW is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeReleaseSavepoint handles *tree.ReleaseSavepoint nodes.
|
||||
func nodeReleaseSavepoint(ctx *Context, node *tree.ReleaseSavepoint) (*vitess.ReleaseSavepoint, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &vitess.ReleaseSavepoint{
|
||||
Identifier: string(node.Savepoint),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRelocate handles *tree.Relocate nodes.
|
||||
func nodeRelocate(ctx *Context, node *tree.Relocate) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("ALTER TABLE RELOCATE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRenameColumn handles *tree.RenameColumn nodes.
|
||||
func nodeRenameColumn(ctx *Context, node *tree.RenameColumn) (*vitess.AlterTable, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
tableName, err := nodeTableName(ctx, &node.Table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.AlterTable{
|
||||
Table: tableName,
|
||||
Statements: []*vitess.DDL{
|
||||
{
|
||||
Action: vitess.AlterStr,
|
||||
ColumnAction: vitess.RenameStr,
|
||||
Table: tableName,
|
||||
Column: vitess.NewColIdent(string(node.Name)),
|
||||
ToColumn: vitess.NewColIdent(string(node.NewName)),
|
||||
IfExists: node.IfExists,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRenameDatabase handles *tree.RenameDatabase nodes.
|
||||
func nodeRenameDatabase(ctx *Context, node *tree.RenameDatabase) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("RENAME DATABASE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRenameIndex handles *tree.RenameIndex nodes.
|
||||
func nodeRenameIndex(ctx *Context, node *tree.RenameIndex) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("RENAME INDEX is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// nodeRenameTable handles *tree.RenameTable nodes.
|
||||
func nodeRenameTable(ctx *Context, node *tree.RenameTable) (*vitess.DDL, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if node.IsSequence {
|
||||
return nil, errors.Errorf("RENAME SEQUENCE is not yet supported")
|
||||
}
|
||||
if node.IsMaterialized {
|
||||
return nil, errors.Errorf("RENAME MATERIALIZED VIEW is not yet supported")
|
||||
}
|
||||
fromName, err := nodeUnresolvedObjectName(ctx, node.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toName, err := nodeUnresolvedObjectName(ctx, node.NewName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vitess.DDL{
|
||||
Action: vitess.RenameStr,
|
||||
FromTables: vitess.TableNames{fromName},
|
||||
ToTables: vitess.TableNames{toName},
|
||||
IfExists: node.IfExists,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeReparentDatabase handles *tree.ReparentDatabase nodes.
|
||||
func nodeReparentDatabase(ctx *Context, node *tree.ReparentDatabase) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("reparenting a database is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// 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 (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/lib/pq/oid"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/types"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// nodeResolvableTypeReference handles tree.ResolvableTypeReference nodes.
|
||||
func nodeResolvableTypeReference(ctx *Context, typ tree.ResolvableTypeReference, mayBeTrigger bool) (*vitess.ConvertType, *pgtypes.DoltgresType, error) {
|
||||
if typ == nil {
|
||||
// TODO: use UNKNOWN?
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var columnTypeName string
|
||||
var columnTypeLength *vitess.SQLVal
|
||||
var columnTypeScale *vitess.SQLVal
|
||||
var doltgresType *pgtypes.DoltgresType
|
||||
var err error
|
||||
switch columnType := typ.(type) {
|
||||
case *tree.ArrayTypeReference:
|
||||
if uon, ok := columnType.ElementType.(*tree.UnresolvedObjectName); ok {
|
||||
tn := uon.ToTableName()
|
||||
columnTypeName = tn.Object() + "[]"
|
||||
doltgresType = pgtypes.NewUnresolvedArrayDoltgresType(tn.Schema(), tn.Object())
|
||||
} else {
|
||||
return nil, nil, errors.Errorf("the given array type is not yet supported")
|
||||
}
|
||||
case *tree.OIDTypeReference:
|
||||
return nil, nil, errors.Errorf("referencing types by their OID is not yet supported")
|
||||
case *tree.UnresolvedObjectName:
|
||||
tn := columnType.ToTableName()
|
||||
columnTypeName = tn.Object()
|
||||
doltgresType = pgtypes.NewUnresolvedDoltgresType(tn.Schema(), columnTypeName)
|
||||
case *types.GeoMetadata:
|
||||
return nil, nil, errors.Errorf("geometry types are not yet supported")
|
||||
case *types.T:
|
||||
columnTypeName = columnType.SQLStandardName()
|
||||
if columnType.Family() == types.ArrayFamily {
|
||||
switch columnType.Oid() {
|
||||
case oid.T_int2vector:
|
||||
doltgresType = pgtypes.Int16vector
|
||||
case oid.T_oidvector:
|
||||
doltgresType = pgtypes.Oidvector
|
||||
default:
|
||||
_, baseResolvedType, err := nodeResolvableTypeReference(ctx, columnType.ArrayContents(), mayBeTrigger)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if baseResolvedType.IsResolvedType() {
|
||||
// currently the built-in types will be resolved, so it can retrieve its array type
|
||||
doltgresType = baseResolvedType.ToArrayType()
|
||||
} else {
|
||||
// User-defined element type: build an unresolved array type with the proper structure.
|
||||
doltgresType = pgtypes.NewUnresolvedArrayDoltgresType(baseResolvedType.ID.SchemaName(), baseResolvedType.ID.TypeName())
|
||||
}
|
||||
}
|
||||
} else if columnType.Family() == types.GeometryFamily {
|
||||
return nil, nil, errors.Errorf("geometry types are not yet supported")
|
||||
} else if columnType.Family() == types.GeographyFamily {
|
||||
return nil, nil, errors.Errorf("geography types are not yet supported")
|
||||
} else {
|
||||
switch columnType.Oid() {
|
||||
case oid.T_record:
|
||||
doltgresType = pgtypes.Record
|
||||
case oid.T_bool:
|
||||
doltgresType = pgtypes.Bool
|
||||
case oid.T_bytea:
|
||||
doltgresType = pgtypes.Bytea
|
||||
case oid.T_bpchar:
|
||||
width := uint32(columnType.Width())
|
||||
if width > pgtypes.StringMaxLength {
|
||||
return nil, nil, errors.Errorf("length for type bpchar cannot exceed %d", pgtypes.StringMaxLength)
|
||||
} else if width == 0 {
|
||||
// TODO: need to differentiate between definitions 'bpchar' (valid) and 'char(0)' (invalid)
|
||||
doltgresType = pgtypes.BpChar
|
||||
} else {
|
||||
doltgresType, err = pgtypes.NewCharType(int32(width))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
case oid.T_char:
|
||||
width := uint32(columnType.Width())
|
||||
if width > pgtypes.InternalCharLength {
|
||||
return nil, nil, errors.Errorf("length for type \"char\" cannot exceed %d", pgtypes.InternalCharLength)
|
||||
}
|
||||
if width == 0 {
|
||||
width = 1
|
||||
}
|
||||
doltgresType = pgtypes.InternalChar
|
||||
case oid.T_date:
|
||||
doltgresType = pgtypes.Date
|
||||
case oid.T_float4:
|
||||
doltgresType = pgtypes.Float32
|
||||
case oid.T_float8:
|
||||
doltgresType = pgtypes.Float64
|
||||
case oid.T_int2:
|
||||
doltgresType = pgtypes.Int16
|
||||
case oid.T_int2vector:
|
||||
doltgresType = pgtypes.Int16vector
|
||||
case oid.T_int4:
|
||||
doltgresType = pgtypes.Int32
|
||||
case oid.T_int8:
|
||||
doltgresType = pgtypes.Int64
|
||||
case oid.T_interval:
|
||||
doltgresType = pgtypes.Interval
|
||||
case oid.T_json:
|
||||
doltgresType = pgtypes.Json
|
||||
case oid.T_jsonb:
|
||||
doltgresType = pgtypes.JsonB
|
||||
case oid.T_name:
|
||||
doltgresType = pgtypes.Name
|
||||
case oid.T_numeric:
|
||||
if columnType.Precision() == 0 && columnType.Scale() == 0 {
|
||||
doltgresType = pgtypes.Numeric
|
||||
} else {
|
||||
doltgresType, err = pgtypes.NewNumericTypeWithPrecisionAndScale(columnType.Precision(), columnType.Scale())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
case oid.T_oid:
|
||||
doltgresType = pgtypes.Oid
|
||||
case oid.T_oidvector:
|
||||
doltgresType = pgtypes.Oidvector
|
||||
case oid.T_regclass:
|
||||
doltgresType = pgtypes.Regclass
|
||||
case oid.T_regproc:
|
||||
doltgresType = pgtypes.Regproc
|
||||
case oid.T_regtype:
|
||||
doltgresType = pgtypes.Regtype
|
||||
case oid.T_text:
|
||||
doltgresType = pgtypes.Text
|
||||
case oid.T_time:
|
||||
doltgresType = pgtypes.Time
|
||||
case oid.T_timestamp:
|
||||
doltgresType = pgtypes.Timestamp
|
||||
case oid.T_timestamptz:
|
||||
doltgresType = pgtypes.TimestampTZ
|
||||
case oid.T_timetz:
|
||||
doltgresType = pgtypes.TimeTZ
|
||||
case oid.T_uuid:
|
||||
doltgresType = pgtypes.Uuid
|
||||
case oid.T_varchar:
|
||||
width := uint32(columnType.Width())
|
||||
if width > pgtypes.StringMaxLength {
|
||||
return nil, nil, errors.Errorf("length for type varchar cannot exceed %d", pgtypes.StringMaxLength)
|
||||
} else if width == 0 {
|
||||
// TODO: need to differentiate between definitions 'varchar' (valid) and 'varchar(0)' (invalid)
|
||||
doltgresType = pgtypes.VarChar
|
||||
} else {
|
||||
doltgresType, err = pgtypes.NewVarCharType(int32(width))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
case oid.T_xid:
|
||||
doltgresType = pgtypes.Xid
|
||||
case oid.T_bit:
|
||||
width := uint32(columnType.Width())
|
||||
if width > pgtypes.StringMaxLength {
|
||||
return nil, nil, errors.Errorf("length for type bit cannot exceed %d", pgtypes.StringMaxLength)
|
||||
} else if width == 0 {
|
||||
// TODO: need to differentiate between definitions 'bit' (valid) and 'bit(0)' (invalid)
|
||||
doltgresType = pgtypes.Bit
|
||||
} else {
|
||||
doltgresType, err = pgtypes.NewBitType(int32(width))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
case oid.T_varbit:
|
||||
width := uint32(columnType.Width())
|
||||
if width > pgtypes.StringMaxLength {
|
||||
return nil, nil, errors.Errorf("length for type varbit cannot exceed %d", pgtypes.StringMaxLength)
|
||||
} else if width == 0 {
|
||||
// TODO: need to differentiate between definitions 'varbit' (valid) and 'varbit(0)' (invalid)
|
||||
doltgresType = pgtypes.VarBit
|
||||
} else {
|
||||
doltgresType, err = pgtypes.NewVarBitType(int32(width))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
default:
|
||||
doltgresType = pgtypes.NewUnresolvedDoltgresType("", strings.ToLower(columnType.Name()))
|
||||
}
|
||||
}
|
||||
default:
|
||||
doltgresType = pgtypes.NewUnresolvedDoltgresType("", strings.ToLower(typ.SQLString()))
|
||||
}
|
||||
|
||||
return &vitess.ConvertType{
|
||||
Type: columnTypeName,
|
||||
Length: columnTypeLength,
|
||||
Scale: columnTypeScale,
|
||||
Charset: "", // TODO
|
||||
}, doltgresType, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeRestore handles *tree.Restore nodes.
|
||||
func nodeRestore(ctx *Context, node *tree.Restore) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return NotYetSupportedError("RESTORE is not yet supported")
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 ast
|
||||
|
||||
import (
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
)
|
||||
|
||||
// nodeReturn handles *tree.Return nodes.
|
||||
func nodeReturn(ctx *Context, node *tree.Return) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
expr, err := nodeExpr(ctx, node.Expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vitess.InjectedStatement{
|
||||
Statement: pgnodes.NewReturn(node.Expr.String()),
|
||||
Children: []vitess.Expr{expr},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/privilege"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
pgnodes "github.com/dolthub/doltgresql/server/node"
|
||||
)
|
||||
|
||||
// nodeRevoke handles *tree.Revoke nodes.
|
||||
func nodeRevoke(ctx *Context, node *tree.Revoke) (vitess.Statement, error) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var revokeTable *pgnodes.RevokeTable
|
||||
var revokeSchema *pgnodes.RevokeSchema
|
||||
var revokeDatabase *pgnodes.RevokeDatabase
|
||||
var revokeSequence *pgnodes.RevokeSequence
|
||||
var revokeRoutine *pgnodes.RevokeRoutine
|
||||
switch node.Targets.TargetType {
|
||||
case privilege.Table:
|
||||
tables := make([]doltdb.TableName, len(node.Targets.Tables)+len(node.Targets.InSchema))
|
||||
for i, table := range node.Targets.Tables {
|
||||
normalizedTable, err := table.NormalizeTablePattern()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch normalizedTable := normalizedTable.(type) {
|
||||
case *tree.TableName:
|
||||
if normalizedTable.ExplicitCatalog {
|
||||
return nil, errors.Errorf("revoking privileges from other databases is not yet supported")
|
||||
}
|
||||
tables[i] = doltdb.TableName{
|
||||
Name: string(normalizedTable.ObjectName),
|
||||
Schema: string(normalizedTable.SchemaName),
|
||||
}
|
||||
case *tree.AllTablesSelector:
|
||||
tables[i] = doltdb.TableName{
|
||||
Name: "",
|
||||
Schema: string(normalizedTable.SchemaName),
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf(`unexpected table type in REVOKE: %T`, normalizedTable)
|
||||
}
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
tables = append(tables, doltdb.TableName{
|
||||
Name: "",
|
||||
Schema: schema,
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_TABLE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokeTable = &pgnodes.RevokeTable{
|
||||
Privileges: privileges,
|
||||
Tables: tables,
|
||||
}
|
||||
case privilege.Schema:
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SCHEMA, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokeSchema = &pgnodes.RevokeSchema{
|
||||
Privileges: privileges,
|
||||
Schemas: node.Targets.Names,
|
||||
}
|
||||
case privilege.Database:
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_DATABASE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokeDatabase = &pgnodes.RevokeDatabase{
|
||||
Privileges: privileges,
|
||||
Databases: node.Targets.Databases.ToStrings(),
|
||||
}
|
||||
case privilege.Sequence:
|
||||
sequences := make([]auth.SequencePrivilegeKey, 0, len(node.Targets.Sequences)+len(node.Targets.InSchema))
|
||||
for _, seq := range node.Targets.Sequences {
|
||||
sequences = append(sequences, auth.SequencePrivilegeKey{
|
||||
Schema: sequenceSchema(seq),
|
||||
Name: seq.Parts[0],
|
||||
})
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
sequences = append(sequences, auth.SequencePrivilegeKey{
|
||||
Schema: schema,
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SEQUENCE, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokeSequence = &pgnodes.RevokeSequence{
|
||||
Privileges: privileges,
|
||||
Sequences: sequences,
|
||||
}
|
||||
case privilege.Function, privilege.Procedure, privilege.Routine:
|
||||
routines := make([]auth.RoutinePrivilegeKey, 0, len(node.Targets.Routines)+len(node.Targets.InSchema))
|
||||
for _, r := range node.Targets.Routines {
|
||||
routines = append(routines, auth.RoutinePrivilegeKey{
|
||||
Schema: routineSchema(r.Name),
|
||||
Name: r.Name.Parts[0],
|
||||
// TODO: there can be 2 routines with the same name but different argument types
|
||||
// need a fix for getting argument types from parsing CALL statement
|
||||
//ArgTypes: routineArgTypesKey(r.Args),
|
||||
})
|
||||
}
|
||||
for _, schema := range node.Targets.InSchema {
|
||||
routines = append(routines, auth.RoutinePrivilegeKey{
|
||||
Schema: schema,
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_FUNCTION, node.Privileges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokeRoutine = &pgnodes.RevokeRoutine{
|
||||
Privileges: privileges,
|
||||
Routines: routines,
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("this form of REVOKE is not yet supported")
|
||||
}
|
||||
return vitess.InjectedStatement{
|
||||
Statement: &pgnodes.Revoke{
|
||||
RevokeTable: revokeTable,
|
||||
RevokeSchema: revokeSchema,
|
||||
RevokeDatabase: revokeDatabase,
|
||||
RevokeSequence: revokeSequence,
|
||||
RevokeRoutine: revokeRoutine,
|
||||
RevokeRole: nil,
|
||||
FromRoles: node.Grantees,
|
||||
GrantedBy: node.GrantedBy,
|
||||
GrantOptionFor: node.GrantOptionFor,
|
||||
Cascade: node.DropBehavior == tree.DropCascade,
|
||||
},
|
||||
Children: nil,
|
||||
}, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user