Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: pg_proc and SHOW CREATE FUNCTION #85656

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/ccl/changefeedccl/cdceval/func_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func (rs *CDCFunctionResolver) WrapFunction(name string) (*tree.ResolvedFunction
// ResolveFunctionByOID implements FunctionReferenceResolver interface.
func (rs *CDCFunctionResolver) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (*tree.Overload, error) {
) (string, *tree.Overload, error) {
// CDC doesn't support user defined function yet, so there's no need to
// resolve function by OID.
return nil, errors.AssertionFailedf("unimplemented yet")
return "", nil, errors.AssertionFailedf("unimplemented yet")
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ crdb_internal cluster_settings table admin NULL NULL
crdb_internal cluster_statement_statistics table admin NULL NULL
crdb_internal cluster_transaction_statistics table admin NULL NULL
crdb_internal cluster_transactions table admin NULL NULL
crdb_internal create_function_statements table admin NULL NULL
crdb_internal create_schema_statements table admin NULL NULL
crdb_internal create_statements table admin NULL NULL
crdb_internal create_type_statements table admin NULL NULL
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/testdata/zip/partial1
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ debug zip --concurrency=1 --cpu-profile-duration=0s /dev/null
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements... writing output: debug/crdb_internal.create_schema_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_statements... writing output: debug/crdb_internal.create_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_type_statements... writing output: debug/crdb_internal.create_type_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements... writing output: debug/crdb_internal.create_function_statements.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness... writing output: debug/crdb_internal.kv_node_liveness.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_status... writing output: debug/crdb_internal.kv_node_status.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_store_status... writing output: debug/crdb_internal.kv_store_status.txt... done
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/testdata/zip/partial1_excluded
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ debug zip /dev/null --concurrency=1 --exclude-nodes=2 --cpu-profile-duration=0
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements... writing output: debug/crdb_internal.create_schema_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_statements... writing output: debug/crdb_internal.create_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_type_statements... writing output: debug/crdb_internal.create_type_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements... writing output: debug/crdb_internal.create_function_statements.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness... writing output: debug/crdb_internal.kv_node_liveness.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_status... writing output: debug/crdb_internal.kv_node_status.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_store_status... writing output: debug/crdb_internal.kv_store_status.txt... done
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/testdata/zip/partial2
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ debug zip --concurrency=1 --cpu-profile-duration=0 /dev/null
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements... writing output: debug/crdb_internal.create_schema_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_statements... writing output: debug/crdb_internal.create_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_type_statements... writing output: debug/crdb_internal.create_type_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements... writing output: debug/crdb_internal.create_function_statements.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness... writing output: debug/crdb_internal.kv_node_liveness.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_status... writing output: debug/crdb_internal.kv_node_status.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_store_status... writing output: debug/crdb_internal.kv_store_status.txt... done
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/testdata/zip/testzip
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ debug zip --concurrency=1 --cpu-profile-duration=1s /dev/null
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements... writing output: debug/crdb_internal.create_schema_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_statements... writing output: debug/crdb_internal.create_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_type_statements... writing output: debug/crdb_internal.create_type_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements... writing output: debug/crdb_internal.create_function_statements.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness... writing output: debug/crdb_internal.kv_node_liveness.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_status... writing output: debug/crdb_internal.kv_node_status.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_store_status... writing output: debug/crdb_internal.kv_store_status.txt... done
Expand Down
3 changes: 3 additions & 0 deletions pkg/cli/testdata/zip/testzip_concurrent
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ zip
[cluster] requesting tenant ranges: done
[cluster] requesting tenant ranges: last request failed: rpc error: ...
[cluster] requesting tenant ranges: received response...
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements...
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements: done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements: writing output: debug/crdb_internal.create_function_statements.txt...
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements...
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements: done
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements: writing output: debug/crdb_internal.create_schema_statements.txt...
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/testdata/zip/testzip_tenant
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ debug zip --concurrency=1 --cpu-profile-duration=1s /dev/null
[cluster] retrieving SQL data for "".crdb_internal.create_schema_statements... writing output: debug/crdb_internal.create_schema_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_statements... writing output: debug/crdb_internal.create_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_type_statements... writing output: debug/crdb_internal.create_type_statements.txt... done
[cluster] retrieving SQL data for "".crdb_internal.create_function_statements... writing output: debug/crdb_internal.create_function_statements.txt... done
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness... writing output: debug/crdb_internal.kv_node_liveness.txt...
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness: last request failed: ERROR: unimplemented: operation is unsupported in multi-tenancy mode (SQLSTATE 0A000)
[cluster] retrieving SQL data for crdb_internal.kv_node_liveness: creating error output: debug/crdb_internal.kv_node_liveness.txt.err.txt... done
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/zip_cluster_wide.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ var debugZipTablesPerCluster = []string{
`"".crdb_internal.create_statements`,
// Ditto, for CREATE TYPE.
`"".crdb_internal.create_type_statements`,
`"".crdb_internal.create_function_statements`,

"crdb_internal.kv_node_liveness",
"crdb_internal.kv_node_status",
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/catalog/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,13 @@ type FunctionDescriptor interface {
// ToOverload converts the function descriptor to tree.Overload object which
// can be used for execution.
ToOverload() (ret *tree.Overload, err error)

// GetLanguage returns the language of this function.
GetLanguage() catpb.Function_Language

// ToCreateExpr converts a function descriptor back to a CREATE FUNCTION
// statement. This is mainly used for formatting, e.g. SHOW CREATE FUNCTION.
ToCreateExpr() (*tree.CreateFunction, error)
}

// FilterDescriptorState inspects the state of a given descriptor and returns an
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/catalog/funcdesc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ go_library(
"//pkg/sql/catalog/catpb",
"//pkg/sql/catalog/catprivilege",
"//pkg/sql/catalog/descpb",
"//pkg/sql/parser",
"//pkg/sql/pgwire/pgcode",
"//pkg/sql/pgwire/pgerror",
"//pkg/sql/privilege",
Expand Down
86 changes: 86 additions & 0 deletions pkg/sql/catalog/funcdesc/func_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/catalog/catpb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/catprivilege"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/parser"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
"github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb"
"github.com/cockroachdb/cockroach/pkg/sql/sem/catid"
Expand Down Expand Up @@ -457,6 +458,11 @@ func (desc *immutable) FuncDesc() *descpb.FunctionDescriptor {
return &desc.FunctionDescriptor
}

// GetLanguage implements the FunctionDescriptor interface.
func (desc *immutable) GetLanguage() catpb.Function_Language {
return desc.Lang
}

// ContainsUserDefinedTypes implements the catalog.HydratableDescriptor interface.
func (desc *immutable) ContainsUserDefinedTypes() bool {
for i := range desc.Args {
Expand Down Expand Up @@ -531,6 +537,86 @@ func (desc *immutable) calledOnNullInput() (bool, error) {
}
}

// ToCreateExpr implements the FunctionDescriptor interface.
func (desc *immutable) ToCreateExpr() (ret *tree.CreateFunction, err error) {
ret = &tree.CreateFunction{
FuncName: tree.MakeFunctionNameFromPrefix(tree.ObjectNamePrefix{}, tree.Name(desc.Name)),
ReturnType: tree.FuncReturnType{
Type: desc.ReturnType.Type,
IsSet: desc.ReturnType.ReturnSet,
},
}
ret.Args = make(tree.FuncArgs, len(desc.Args))
for i := range desc.Args {
ret.Args[i] = tree.FuncArg{
Name: tree.Name(desc.Args[i].Name),
Type: desc.Args[i].Type,
Class: toTreeNodeArgClass(desc.Args[i].Class),
}
if desc.Args[i].DefaultExpr != nil {
ret.Args[i].DefaultVal, err = parser.ParseExpr(*desc.Args[i].DefaultExpr)
if err != nil {
return nil, err
}
}
}
// We only store 5 function attributes at the moment. We may extend the
// pre-allocated capacity in the future.
ret.Options = make(tree.FunctionOptions, 0, 5)
ret.Options = append(ret.Options, desc.getCreateExprVolatility())
ret.Options = append(ret.Options, tree.FunctionLeakproof(desc.LeakProof))
ret.Options = append(ret.Options, desc.getCreateExprNullInputBehavior())
ret.Options = append(ret.Options, tree.FunctionBodyStr(desc.FunctionBody))
ret.Options = append(ret.Options, desc.getCreateExprLang())
return ret, nil
}

func (desc *immutable) getCreateExprLang() tree.FunctionLanguage {
switch desc.Lang {
case catpb.Function_SQL:
return tree.FunctionLangSQL
}
return 0
}

func (desc *immutable) getCreateExprVolatility() tree.FunctionVolatility {
switch desc.Volatility {
case catpb.Function_IMMUTABLE:
return tree.FunctionImmutable
case catpb.Function_STABLE:
return tree.FunctionStable
case catpb.Function_VOLATILE:
return tree.FunctionVolatile
}
return 0
}

func (desc *immutable) getCreateExprNullInputBehavior() tree.FunctionNullInputBehavior {
switch desc.NullInputBehavior {
case catpb.Function_CALLED_ON_NULL_INPUT:
return tree.FunctionCalledOnNullInput
case catpb.Function_RETURNS_NULL_ON_NULL_INPUT:
return tree.FunctionReturnsNullOnNullInput
case catpb.Function_STRICT:
return tree.FunctionStrict
}
return 0
}

func toTreeNodeArgClass(class catpb.Function_Arg_Class) tree.FuncArgClass {
switch class {
case catpb.Function_Arg_IN:
return tree.FunctionArgIn
case catpb.Function_Arg_OUT:
return tree.FunctionArgOut
case catpb.Function_Arg_IN_OUT:
return tree.FunctionArgInOut
case catpb.Function_Arg_VARIADIC:
return tree.FunctionArgVariadic
}
return 0
}

// UserDefinedFunctionOIDToID converts a UDF OID into a descriptor ID. OID of a
// UDF must be greater CockroachPredefinedOIDMax. The function returns an error
// if the given OID is less than or equal to CockroachPredefinedOIDMax.
Expand Down
4 changes: 4 additions & 0 deletions pkg/sql/catalog/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type SchemaDescriptor interface {
// returns a collection of overloads with the same function name, each
// overload is prefixed with the same schema name.
GetResolvedFuncDefinition(name string) (*tree.ResolvedFunctionDefinition, bool)

// ForEachFunctionOverload iterates through all function overloads within the
// schema and calls fn on each overload.
ForEachFunctionOverload(fn func(overload descpb.SchemaDescriptor_FunctionOverload) error) error
}

// ResolvedSchemaKind is an enum that represents what kind of schema
Expand Down
14 changes: 14 additions & 0 deletions pkg/sql/catalog/schemadesc/schema_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,20 @@ func (desc *immutable) GetResolvedFuncDefinition(
return funcDef, true
}

// ForEachFunctionOverload implements the SchemaDescriptor interface.
func (desc *immutable) ForEachFunctionOverload(
fn func(overload descpb.SchemaDescriptor_FunctionOverload) error,
) error {
for _, function := range desc.Functions {
for i := range function.Overloads {
if err := fn(function.Overloads[i]); err != nil {
return err
}
}
}
return nil
}

// IsSchemaNameValid returns whether the input name is valid for a user defined
// schema.
func IsSchemaNameValid(name string) error {
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/catalog/schemadesc/synthetic_schema_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ func (p synthetic) GetFunction(name string) (descpb.SchemaDescriptor_Function, b
return descpb.SchemaDescriptor_Function{}, false
}

// ForEachFunctionOverload implements the SchemaDescriptor interface.
func (p synthetic) ForEachFunctionOverload(
fn func(overload descpb.SchemaDescriptor_FunctionOverload) error,
) error {
return nil
}

func (p synthetic) ContainsUserDefinedTypes() bool {
return false
}
Expand Down
1 change: 0 additions & 1 deletion pkg/sql/catalog/typedesc/type_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,6 @@ func EnsureTypeIsHydrated(
return err
}
}
return nil
Copy link
Contributor Author

@chengxiong-ruan chengxiong-ruan Aug 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was a bug that EnsureTypeIsHydrated didn't hydrate table implicit types which is actually a tuple. Let me know if this makes sense..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we make this change, let's also add a test that shows why it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah, good call, I'll add one for it.

}
if !t.UserDefined() || t.IsHydrated() {
return nil
Expand Down
56 changes: 56 additions & 0 deletions pkg/sql/crdb_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ var crdbInternal = virtualSchema{
catconstants.CrdbInternalClusterSessionsTableID: crdbInternalClusterSessionsTable,
catconstants.CrdbInternalClusterSettingsTableID: crdbInternalClusterSettingsTable,
catconstants.CrdbInternalClusterStmtStatsTableID: crdbInternalClusterStmtStatsTable,
catconstants.CrdbInternalCreateFunctionStmtsTableID: crdbInternalCreateFunctionStmtsTable,
catconstants.CrdbInternalCreateSchemaStmtsTableID: crdbInternalCreateSchemaStmtsTable,
catconstants.CrdbInternalCreateStmtsTableID: crdbInternalCreateStmtsTable,
catconstants.CrdbInternalCreateTypeStmtsTableID: crdbInternalCreateTypeStmtsTable,
Expand Down Expand Up @@ -2576,6 +2577,61 @@ CREATE TABLE crdb_internal.create_schema_statements (
},
}

var crdbInternalCreateFunctionStmtsTable = virtualSchemaTable{
comment: "CREATE statements for all user-defined functions",
schema: `
CREATE TABLE crdb_internal.create_function_statements (
database_id INT,
database_name STRING,
schema_id INT,
schema_name STRING,
function_id INT,
function_name STRING,
create_statement STRING
)
`,
populate: func(ctx context.Context, p *planner, db catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error {
return forEachSchema(ctx, p, db, func(sc catalog.SchemaDescriptor) error {
return sc.ForEachFunctionOverload(func(overload descpb.SchemaDescriptor_FunctionOverload) error {
fnDesc, err := p.Descriptors().GetImmutableFunctionByID(ctx, p.txn, overload.ID, tree.ObjectLookupFlags{})
if err != nil {
return err
}
treeNode, err := fnDesc.ToCreateExpr()
treeNode.FuncName.ObjectNamePrefix = tree.ObjectNamePrefix{
ExplicitSchema: true,
SchemaName: tree.Name(sc.GetName()),
}
if err != nil {
return err
}
for i := range treeNode.Options {
if body, ok := treeNode.Options[i].(tree.FunctionBodyStr); ok {
stmtStrs := strings.Split(string(body), "\n")
for i := range stmtStrs {
stmtStrs[i] = "\t" + stmtStrs[i]
}

p := &treeNode.Options[i]
// Add two new lines just for better formatting.
*p = "\n" + tree.FunctionBodyStr(strings.Join(stmtStrs, "\n")) + "\n"
}
}

return addRow(
tree.NewDInt(tree.DInt(db.GetID())), // database_id
tree.NewDString(db.GetName()), // database_name
tree.NewDInt(tree.DInt(sc.GetID())), // schema_id
tree.NewDString(sc.GetName()), // schema_name
tree.NewDInt(tree.DInt(fnDesc.GetID())), // function_id
tree.NewDString(fnDesc.GetName()), //function_name
tree.NewDString(tree.AsString(treeNode)), // create_statement
)
})
})
},
}

// Prepare the row populate function.
var typeView = tree.NewDString("view")
var typeTable = tree.NewDString("table")
Expand Down
36 changes: 34 additions & 2 deletions pkg/sql/delegate/show_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,42 @@
package delegate

import (
"fmt"

"github.com/cockroachdb/cockroach/pkg/sql/lexbase"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
"github.com/cockroachdb/errors"
)

func (d *delegator) delegateShowCreateFunction(n *tree.ShowCreateFunction) (tree.Statement, error) {
return nil, unimplemented.New("SHOW CREATE FUNCTION", "this statement is not yet supported")
// We don't need to filter by db since we don't allow cross-database
// references.
query := `
SELECT function_name, create_statement
FROM crdb_internal.create_function_statements
WHERE schema_name = %[1]s
AND function_name = %[2]s
`
un, ok := n.Name.FunctionReference.(*tree.UnresolvedName)
if !ok {
return nil, errors.AssertionFailedf("not a valid function name")
}

fn, err := d.catalog.ResolveFunction(d.ctx, un, &d.evalCtx.SessionData().SearchPath)
if err != nil {
return nil, err
}

var udfSchema string
for _, o := range fn.Overloads {
if o.IsUDF {
udfSchema = o.Schema
}
}
if udfSchema == "" {
return nil, errors.Errorf("function %s does not exist", tree.AsString(un))
}

fullQuery := fmt.Sprintf(query, lexbase.EscapeSQLString(udfSchema), lexbase.EscapeSQLString(un.Parts[0]))
return parse(fullQuery)
}
14 changes: 14 additions & 0 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,20 @@ func (ep *DummyEvalPlanner) IsActive(_ context.Context, _ clusterversion.Key) bo
return true
}

// ResolveFunction implements FunctionReferenceResolver interface.
func (ep *DummyEvalPlanner) ResolveFunction(
ctx context.Context, name *tree.UnresolvedName, path tree.SearchPath,
) (*tree.ResolvedFunctionDefinition, error) {
return nil, errors.AssertionFailedf("ResolveFunction unimplemented")
}

// ResolveFunctionByOID implements FunctionReferenceResolver interface.
func (ep *DummyEvalPlanner) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
return "", nil, errors.AssertionFailedf("ResolveFunctionByOID unimplemented")
}

// DummyPrivilegedAccessor implements the tree.PrivilegedAccessor interface by returning errors.
type DummyPrivilegedAccessor struct{}

Expand Down
Loading