Skip to content

Commit

Permalink
sql: implement function OID reference
Browse files Browse the repository at this point in the history
This commit implements `[FUNCTION xxxx]` OID references syntax
of functions. With this change, functions can be called with
the new OID numerical representation. For example
`SELECT [FUNCTION 123]('helloworld')`. The intention of this
syntax is only for internal serialization of references to UDFs.
But it's general enough for builtin functions as well.

A new implementation of the `ResolvableFunctionReference`
interface, `OIDFunctionReference` is introduced for function
resolution purpose for the new syntax.

The `ResolveFunctionByOID` method is also refactored to return
a qualified name.

Fixes: cockroachdb#83231

Release note: None
  • Loading branch information
chengxiong-ruan committed Feb 2, 2023
1 parent dc32242 commit a8525d6
Show file tree
Hide file tree
Showing 27 changed files with 336 additions and 89 deletions.
30 changes: 17 additions & 13 deletions docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -3491,11 +3491,11 @@ opt_interval_qualifier ::=
|

func_application ::=
func_name '(' ')'
| func_name '(' expr_list opt_sort_clause ')'
| func_name '(' 'ALL' expr_list opt_sort_clause ')'
| func_name '(' 'DISTINCT' expr_list ')'
| func_name '(' '*' ')'
func_application_name '(' ')'
| func_application_name '(' expr_list opt_sort_clause ')'
| func_application_name '(' 'ALL' expr_list opt_sort_clause ')'
| func_application_name '(' 'DISTINCT' expr_list ')'
| func_application_name '(' '*' ')'

within_group_clause ::=
'WITHIN' 'GROUP' '(' single_sort_clause ')'
Expand Down Expand Up @@ -3747,10 +3747,9 @@ rowsfrom_item ::=
opt_col_def_list ::=
'(' col_def_list ')'

func_name ::=
type_function_name
| prefixed_column_path
| 'INDEX'
func_application_name ::=
func_name
| '[' 'FUNCTION' iconst32 ']'

single_sort_clause ::=
'ORDER' 'BY' sortby
Expand Down Expand Up @@ -3898,10 +3897,10 @@ create_as_params ::=
col_def_list ::=
( col_def ) ( ( ',' col_def ) )*

type_function_name ::=
'identifier'
| unreserved_keyword
| type_func_name_keyword
func_name ::=
type_function_name
| prefixed_column_path
| 'INDEX'

opt_existing_window_name ::=
name
Expand Down Expand Up @@ -3942,6 +3941,11 @@ trim_list ::=
| 'FROM' expr_list
| expr_list

type_function_name ::=
'identifier'
| unreserved_keyword
| type_func_name_keyword

char_aliases ::=
'CHAR'
| 'CHARACTER'
Expand Down
8 changes: 4 additions & 4 deletions pkg/ccl/changefeedccl/cdceval/func_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ func (rs *cdcFunctionResolver) ResolveFunction(
// ResolveFunctionByOID implements FunctionReferenceResolver interface.
func (rs *cdcFunctionResolver) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
) (*tree.FunctionName, *tree.Overload, error) {
fnName, overload, err := rs.wrapped.ResolveFunctionByOID(ctx, oid)
if err != nil {
return "", nil, err
return nil, nil, err
}
if err := checkOverloadSupported(fnName, overload); err != nil {
return "", nil, err
if err := checkOverloadSupported(fnName.Object(), overload); err != nil {
return nil, nil, err
}
return fnName, overload, err
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,8 @@ func (ep *DummyEvalPlanner) ResolveFunction(
// ResolveFunctionByOID implements FunctionReferenceResolver interface.
func (ep *DummyEvalPlanner) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
return "", nil, errors.AssertionFailedf("ResolveFunctionByOID unimplemented")
) (*tree.FunctionName, *tree.Overload, error) {
return nil, nil, errors.AssertionFailedf("ResolveFunctionByOID unimplemented")
}

// GetMultiregionConfig is part of the eval.Planner interface.
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/importer/import_table_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,6 @@ func (r fkResolver) ResolveFunction(
// ResolveFunctionByOID implements the resolver.SchemaResolver interface.
func (r fkResolver) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
return "", nil, errSchemaResolver
) (*tree.FunctionName, *tree.Overload, error) {
return nil, nil, errSchemaResolver
}
54 changes: 54 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/udf_oid_ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
query T
SELECT [FUNCTION 1074]('hello,world', ',')
----
{hello,world}

statement ok
CREATE TABLE t1(a INT PRIMARY KEY, b STRING DEFAULT ([FUNCTION 1074]('hello,world', ',')))

statement ok
INSERT INTO t1(a) VALUES (1)

query IT
SELECT * FROM t1
----
1 {hello,world}

statement ok
INSERT INTO t1 VALUES (2, 'hello,new,world')

statement ok
CREATE INDEX idx ON t1([FUNCTION 1074](b,','))

query IT
SELECT * FROM t1@idx WHERE [FUNCTION 1074](b, ',') = ARRAY['hello','new','world']
----
2 hello,new,world

statement ok
ALTER TABLE t1 ADD CONSTRAINT c_len CHECK ([FUNCTION 814](b) > 2)

statement error pq: failed to satisfy CHECK constraint \(length\(b\) > 2:::INT8\)
INSERT INTO t1 VALUES (3, 'a')

statement ok
CREATE FUNCTION f1() RETURNS INT LANGUAGE SQL AS $$ SELECT 1 $$

let $fn_oid
SELECT oid FROM pg_catalog.pg_proc WHERE proname = 'f1'

query I
SELECT [FUNCTION $fn_oid]()
----
1

statement ok
CREATE FUNCTION f2(a STRING) RETURNS STRING LANGUAGE SQL AS $$ SELECT a $$

let $fn_oid
SELECT oid FROM pg_catalog.pg_proc WHERE proname = 'f2'

query T
SELECT [FUNCTION $fn_oid]('hello world')
----
hello world
7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/sql/opt/cat/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ type Catalog interface {
) (*tree.ResolvedFunctionDefinition, error)

// ResolveFunctionByOID resolves a function overload by OID.
ResolveFunctionByOID(ctx context.Context, oid oid.Oid) (string, *tree.Overload, error)
ResolveFunctionByOID(ctx context.Context, oid oid.Oid) (*tree.FunctionName, *tree.Overload, error)

// CheckPrivilege verifies that the current user has the given privilege on
// the given catalog object. If not, then CheckPrivilege returns an error.
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/testutils/testcat/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func (tc *Catalog) ResolveFunction(
// ResolveFunctionByOID part of the tree.FunctionReferenceResolver interface.
func (tc *Catalog) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
return "", nil, errors.AssertionFailedf("ResolveFunctionByOID not supported in test catalog")
) (*tree.FunctionName, *tree.Overload, error) {
return nil, nil, errors.AssertionFailedf("ResolveFunctionByOID not supported in test catalog")
}

// CreateFunction handles the CREATE FUNCTION statement.
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/opt_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func (oc *optCatalog) ResolveFunction(

func (oc *optCatalog) ResolveFunctionByOID(
ctx context.Context, oid oid.Oid,
) (string, *tree.Overload, error) {
) (*tree.FunctionName, *tree.Overload, error) {
return oc.planner.ResolveFunctionByOID(ctx, oid)
}

Expand Down
45 changes: 31 additions & 14 deletions pkg/sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,9 @@ func (u *sqlSymUnion) scrubOption() tree.ScrubOption {
func (u *sqlSymUnion) resolvableFuncRefFromName() tree.ResolvableFunctionReference {
return tree.ResolvableFunctionReference{FunctionReference: u.unresolvedName()}
}
func (u *sqlSymUnion) resolvableFuncRef() tree.ResolvableFunctionReference {
return u.val.(tree.ResolvableFunctionReference)
}
func (u *sqlSymUnion) rowsFromExpr() *tree.RowsFromExpr {
return u.val.(*tree.RowsFromExpr)
}
Expand Down Expand Up @@ -1370,6 +1373,7 @@ func (u *sqlSymUnion) showTenantOpts() tree.ShowTenantOptions {
%type <tree.KVOption> role_option password_clause valid_until_clause
%type <tree.Operator> subquery_op
%type <*tree.UnresolvedName> func_name func_name_no_crdb_extra
%type <tree.ResolvableFunctionReference> func_application_name
%type <str> opt_class opt_collate

%type <str> cursor_name database_name index_name opt_index_name column_name insert_column_item statistics_name window_name opt_in_database
Expand Down Expand Up @@ -14264,7 +14268,7 @@ d_expr:
if err != nil { return setErr(sqllex, err) }
$$.val = d
}
| func_name '(' expr_list opt_sort_clause ')' SCONST { return unimplemented(sqllex, $1.unresolvedName().String() + "(...) SCONST") }
| func_application_name '(' expr_list opt_sort_clause ')' SCONST { return unimplemented(sqllex, $1.resolvableFuncRef().String() + "(...) SCONST") }
| typed_literal
{
$$.val = $1.expr()
Expand Down Expand Up @@ -14351,31 +14355,44 @@ d_expr:
| GROUPING '(' expr_list ')' { return unimplemented(sqllex, "d_expr grouping") }

func_application:
func_name '(' ')'
func_application_name '(' ')'
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName()}
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRef()}
}
| func_name '(' expr_list opt_sort_clause ')'
| func_application_name '(' expr_list opt_sort_clause ')'
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Exprs: $3.exprs(), OrderBy: $4.orderBy(), AggType: tree.GeneralAgg}
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRef(), Exprs: $3.exprs(), OrderBy: $4.orderBy(), AggType: tree.GeneralAgg}
}
| func_name '(' VARIADIC a_expr opt_sort_clause ')' { return unimplemented(sqllex, "variadic") }
| func_name '(' expr_list ',' VARIADIC a_expr opt_sort_clause ')' { return unimplemented(sqllex, "variadic") }
| func_name '(' ALL expr_list opt_sort_clause ')'
| func_application_name '(' VARIADIC a_expr opt_sort_clause ')' { return unimplemented(sqllex, "variadic") }
| func_application_name '(' expr_list ',' VARIADIC a_expr opt_sort_clause ')' { return unimplemented(sqllex, "variadic") }
| func_application_name '(' ALL expr_list opt_sort_clause ')'
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Type: tree.AllFuncType, Exprs: $4.exprs(), OrderBy: $5.orderBy(), AggType: tree.GeneralAgg}
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRef(), Type: tree.AllFuncType, Exprs: $4.exprs(), OrderBy: $5.orderBy(), AggType: tree.GeneralAgg}
}
// TODO(ridwanmsharif): Once DISTINCT is supported by window aggregates,
// allow ordering to be specified below.
| func_name '(' DISTINCT expr_list ')'
| func_application_name '(' DISTINCT expr_list ')'
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRef(), Type: tree.DistinctFuncType, Exprs: $4.exprs()}
}
| func_application_name '(' '*' ')'
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Type: tree.DistinctFuncType, Exprs: $4.exprs()}
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRef(), Exprs: tree.Exprs{tree.StarExpr()}}
}
| func_name '(' '*' ')'
| func_application_name '(' error { return helpWithFunction(sqllex, $1.resolvableFuncRef()) }

func_application_name:
func_name
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Exprs: tree.Exprs{tree.StarExpr()}}
$$.val = $1.resolvableFuncRefFromName()
}
| '[' FUNCTION iconst32 ']'
{
id := $3.int32()
$$.val = tree.ResolvableFunctionReference{
FunctionReference: &tree.OIDFunctionReference{OID: oid.Oid(id)},
}
}
| func_name '(' error { return helpWithFunction(sqllex, $1.resolvableFuncRefFromName()) }

// typed_literal represents expressions like INT '4', or generally <TYPE> SCONST.
// This rule handles both the case of qualified and non-qualified typenames.
Expand Down
Loading

0 comments on commit a8525d6

Please sign in to comment.