Skip to content

Commit

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

Fixes: cockroachdb#83231

Release note: None
  • Loading branch information
chengxiong-ruan committed Feb 2, 2023
1 parent dc32242 commit ad33f14
Show file tree
Hide file tree
Showing 28 changed files with 341 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
| 'OID_FUNCTION_REF' 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 @F1074('hello,world', ',')
----
{hello,world}

statement ok
CREATE TABLE t1(a INT PRIMARY KEY, b STRING DEFAULT (@F1074('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(@F1074(b,','))

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

statement ok
ALTER TABLE t1 ADD CONSTRAINT c_len CHECK (@F814(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 @F$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 @F$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
46 changes: 32 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 @@ -948,6 +951,7 @@ func (u *sqlSymUnion) showTenantOpts() tree.ShowTenantOptions {

%token <str> OF OFF OFFSET OID OIDS OIDVECTOR OLD_KMS ON ONLY OPT OPTION OPTIONS OR
%token <str> ORDER ORDINALITY OTHERS OUT OUTER OVER OVERLAPS OVERLAY OWNED OWNER OPERATOR
%token <str> OID_FUNCTION_REF

%token <str> PARALLEL PARENT PARTIAL PARTITION PARTITIONS PASSWORD PAUSE PAUSED PHYSICAL PLACEMENT PLACING
%token <str> PLAN PLANS POINT POINTM POINTZ POINTZM POLYGON POLYGONM POLYGONZ POLYGONZM
Expand Down Expand Up @@ -1370,6 +1374,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 +14269,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 +14356,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.resolvableFuncRef(), Exprs: tree.Exprs{tree.StarExpr()}}
}
| func_application_name '(' error { return helpWithFunction(sqllex, $1.resolvableFuncRef()) }

func_application_name:
func_name
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Type: tree.DistinctFuncType, Exprs: $4.exprs()}
$$.val = $1.resolvableFuncRefFromName()
}
| func_name '(' '*' ')'
| OID_FUNCTION_REF iconst32
{
$$.val = &tree.FuncExpr{Func: $1.resolvableFuncRefFromName(), Exprs: tree.Exprs{tree.StarExpr()}}
id := $2.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 ad33f14

Please sign in to comment.