From 83fca32a5fac8c3b657f76fbe14f853553ffdc23 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Thu, 12 Jan 2023 13:00:32 -0500 Subject: [PATCH] comprules: support completion for qualified functions The syntax completion engine now generates suggestion for user-defined functions. It restricts the suggestions to only functions in the schema name if one was provided, or else to the schemas in the search_path. No release note since this functionality is new in v23.1. Release note: None --- .../testdata/logic_test/crdb_internal_tenant | 4 +- pkg/cli/clisqlshell/sql.go | 17 +- .../testdata/complete/composite_names | 52 ++++ pkg/cli/clisqlshell/testdata/complete/sql | 10 + pkg/sql/comprules/rules.go | 54 ++-- .../testdata/completion_patterns/builtins | 248 ++++++++++++++---- pkg/sql/crdb_internal.go | 10 +- .../testdata/logic_test/crdb_internal | 4 +- .../testdata/logic_test/crdb_internal_catalog | 2 +- pkg/sql/pg_catalog.go | 5 +- 10 files changed, 310 insertions(+), 96 deletions(-) diff --git a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant index ce0ab01e4e7f..a3b3fd72ddfe 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant +++ b/pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant @@ -261,10 +261,10 @@ SELECT * FROM crdb_internal.cluster_contention_events WHERE table_id < 0 ---- table_id index_id num_contention_events cumulative_contention_time key txn_id count -query TTTT colnames +query TTTTT colnames SELECT * FROM crdb_internal.builtin_functions WHERE function = '' ---- -function signature category details +function signature category details schema query ITTITTTTTTTBBBB colnames SELECT * FROM crdb_internal.create_statements WHERE database_name = '' diff --git a/pkg/cli/clisqlshell/sql.go b/pkg/cli/clisqlshell/sql.go index 0a98a5c1f3d0..7321c7f25412 100644 --- a/pkg/cli/clisqlshell/sql.go +++ b/pkg/cli/clisqlshell/sql.go @@ -1326,7 +1326,22 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu case `\hf`: if len(cmd) == 1 { - c.concatLines = `SELECT DISTINCT proname AS function FROM pg_proc ORDER BY 1` + // The following query lists all functions. It prefixes + // functions with their schema but only if the schema is not in + // the search path. This ensures that "common" functions + // are not prefixed by "pg_catalog", but crdb_internal functions + // get prefixed with "crdb_internal." + // + // TODO(knz): Replace this by the \df logic when that is implemented; + // see: https://github.com/cockroachdb/cockroach/pull/88061 + c.concatLines = ` +SELECT DISTINCT + IF(n.nspname = ANY current_schemas(TRUE), '', + pg_catalog.quote_ident(n.nspname) || '.') || + pg_catalog.quote_ident(p.proname) AS function + FROM pg_catalog.pg_proc p + JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace +ORDER BY 1` return cliRunStatement } return c.handleFunctionHelp(cmd[1:], loopState, errState) diff --git a/pkg/cli/clisqlshell/testdata/complete/composite_names b/pkg/cli/clisqlshell/testdata/complete/composite_names index 091ec32b7aac..5ee569001043 100644 --- a/pkg/cli/clisqlshell/testdata/complete/composite_names +++ b/pkg/cli/clisqlshell/testdata/complete/composite_names @@ -11,3 +11,55 @@ msg: "" completions: - "functions": "crdb_internal.force_error(" (This function is used only by CockroachDB's developers for testing purposes.) -> "crdb_internal.force_error(" (0, 25) + +sql +create schema my_schema +---- +ok + +sql +CREATE FUNCTION my_func(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT i' +---- +ok + +sql +CREATE FUNCTION my_schema.my_other_func(i INT) RETURNS INT LANGUAGE SQL AS 'SELECT i' +---- +ok + +complete +select my@ +---- +complete 0 9 +msg: "" +completions: +- "functions": + "my_func(" () -> "my_func(" (0, 2) +- "schema": + "my_schema" () -> "my_schema" (0, 2) + +complete +select my_schema.@ +---- +complete 0 17 +msg: "" +completions: +- "functions": + "my_schema.my_other_func(" () -> "my_schema.my_other_func(" (0, 10) + +complete +select pg_catalog.array_l@ +---- +complete 0 25 +msg: "" +completions: +- "functions": + "pg_catalog.array_length(" (Calculates the length of `input` on the provided `array_dimension`. However, bec) -> "pg_catalog.array_length(" (0, 18) + "pg_catalog.array_lower(" (Calculates the minimum value of `input` on the provided `array_dimension`. Howev) -> "pg_catalog.array_lower(" (0, 18) + +complete +select "PG_CATALOG".@ +---- +complete 0 20 +msg: "" +(no completions generated) diff --git a/pkg/cli/clisqlshell/testdata/complete/sql b/pkg/cli/clisqlshell/testdata/complete/sql index d1d22046527f..805af1ed4b93 100644 --- a/pkg/cli/clisqlshell/testdata/complete/sql +++ b/pkg/cli/clisqlshell/testdata/complete/sql @@ -102,3 +102,13 @@ msg: "" completions: - "relation": "mytable" () -> "mytable" (0, 0) + +complete +select array_l@ +---- +complete 0 14 +msg: "" +completions: +- "functions": + "array_length(" (Calculates the length of `input` on the provided `array_dimension`. However, bec) -> "array_length(" (0, 7) + "array_lower(" (Calculates the minimum value of `input` on the provided `array_dimension`. Howev) -> "array_lower(" (0, 7) diff --git a/pkg/sql/comprules/rules.go b/pkg/sql/comprules/rules.go index 01f3b536d135..1552a78c0721 100644 --- a/pkg/sql/comprules/rules.go +++ b/pkg/sql/comprules/rules.go @@ -25,7 +25,7 @@ import ( func GetCompMethods() []compengine.Method { return []compengine.Method{ method("keywords", completeKeyword), - method("builtins", completeBuiltin), + method("functions", completeFunction), method("objects", completeObjectInCurrentDatabase), method("schemas", completeSchemaInCurrentDatabase), method("dbs", completeDatabase), @@ -96,41 +96,27 @@ var compNotQualProcRe = regexp.MustCompile(`[^.](i'|_)`) // A qualified possible builtin name. var compMaybeQualProcRe = regexp.MustCompile(`i\.['_]|i\.i'`) -var compVSchemaRe = regexp.MustCompile(`pg_catalog|crdb_internal|information_schema`) - -func completeBuiltin(ctx context.Context, c compengine.Context) (compengine.Rows, error) { - // Complete builtin names: +func completeFunction(ctx context.Context, c compengine.Context) (compengine.Rows, error) { + // Complete function names: // // - at whitespace after keywords. - // - after a period, if the identifier before the period is a vschema. + // - after a period. // var prefix string var start, end int - var extraPrefix string + var schemaName string atWord := c.AtWord() sketch := c.Sketch() switch { - case compMaybeQualProcRe.MatchString(sketch) && - ((!atWord && compVSchemaRe.MatchString(c.RelToken(-1).Str)) || - (atWord && compVSchemaRe.MatchString(c.RelToken(-2).Str))): + case compMaybeQualProcRe.MatchString(sketch): start = int(c.RelToken(-1).Start) - prefix = c.RelToken(-1).Str + "." + schemaName = c.RelToken(-1).Str if atWord { start = int(c.RelToken(-2).Start) - prefix = c.RelToken(-2).Str + "." - } - // crdb has this weird thing where every unqualified built-in "X" - // also exists as "pg_catalog.X". So when we search for - // completions after "pg_catalog.", we can strip that prefix from - // the search. However, we must be careful to add it back in the - // completion results, so that the prefix does not get stripped - // when the completion occurs. - if prefix == "pg_catalog." { - prefix = "" - extraPrefix = "pg_catalog." + schemaName = c.RelToken(-2).Str } if atWord { - prefix += c.RelToken(0).Str + prefix = c.RelToken(0).Str } end = int(c.RelToken(0).End) @@ -152,23 +138,31 @@ func completeBuiltin(ctx context.Context, c compengine.Context) (compengine.Rows return nil, nil } - c.Trace("completing for %q (%d,%d)", prefix, start, end) + c.Trace("completing for %q (%d,%d) with schema %q", prefix, start, end, schemaName) // TODO(knz): use the comment extraction functions from pg_catalog // instead of crdb_internal. This requires exposing comments for // built-in functions through pg_catalog. const query = ` -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p - WHERE left(proname, length($1:::STRING)) = $1:::STRING` - iter, err := c.Query(ctx, query, prefix, start, end, extraPrefix) + WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true)))` + iter, err := c.Query(ctx, query, prefix, start, end, schemaName) return iter, err } diff --git a/pkg/sql/comprules/testdata/completion_patterns/builtins b/pkg/sql/comprules/testdata/completion_patterns/builtins index 3695dc829a93..43a3e36cee69 100644 --- a/pkg/sql/comprules/testdata/completion_patterns/builtins +++ b/pkg/sql/comprules/testdata/completion_patterns/builtins @@ -1,5 +1,5 @@ filter -builtins: +functions: ---- subtest at_ident @@ -10,19 +10,27 @@ select xor ii' ^ -- -builtins: completing for "xor" (7,10) +functions: completing for "xor" (7,10) with schema "" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) --placeholders: []interface {}{"xor", 7, 10, ""} comp at=12 @@ -31,19 +39,27 @@ select xor ii_ ^ -- -builtins: completing for "" (12,12) +functions: completing for "" (12,12) with schema "" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) --placeholders: []interface {}{"", 12, 12, ""} subtest end @@ -56,7 +72,28 @@ select a.xor ii.i' ^ -- -builtins: not completing +functions: completing for "xor" (7,12) with schema "a" +--sql: +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, + 'functions' AS category, + substr(COALESCE(( + SELECT details + FROM "".crdb_internal.builtin_functions f2 + WHERE f2.function = p.proname AND f2.schema = p.nspname + LIMIT 1), ''), e'[^\n]{0,80}') AS description, + $2:::INT AS start, + $3:::INT AS end + FROM p + WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 12, "a"} comp at=10 select a. @@ -64,7 +101,28 @@ select a. ii._ ^ -- -builtins: not completing +functions: completing for "" (7,9) with schema "a" +--sql: +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, + 'functions' AS category, + substr(COALESCE(( + SELECT details + FROM "".crdb_internal.builtin_functions f2 + WHERE f2.function = p.proname AND f2.schema = p.nspname + LIMIT 1), ''), e'[^\n]{0,80}') AS description, + $2:::INT AS start, + $3:::INT AS end + FROM p + WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"", 7, 9, "a"} subtest end @@ -76,20 +134,28 @@ select crdb_internal.xor ii.i' ^ -- -builtins: completing for "crdb_internal.xor" (7,24) +functions: completing for "xor" (7,24) with schema "crdb_internal" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"crdb_internal.xor", 7, 24, ""} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 24, "crdb_internal"} comp at=21 select crdb_internal. @@ -97,20 +163,28 @@ select crdb_internal. ii.' ^ -- -builtins: completing for "crdb_internal." (7,21) +functions: completing for "" (7,21) with schema "crdb_internal" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"crdb_internal.", 7, 21, ""} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"", 7, 21, "crdb_internal"} comp at=20 @@ -119,20 +193,28 @@ select pg_catalog.xor ii.i' ^ -- -builtins: completing for "xor" (7,21) +functions: completing for "xor" (7,21) with schema "pg_catalog" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"xor", 7, 21, "pg_catalog."} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 21, "pg_catalog"} comp at=18 select pg_catalog. @@ -140,20 +222,28 @@ select pg_catalog. ii.' ^ -- -builtins: completing for "" (7,18) +functions: completing for "" (7,18) with schema "pg_catalog" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"", 7, 18, "pg_catalog."} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"", 7, 18, "pg_catalog"} comp at=27 @@ -162,20 +252,28 @@ select information_schema.xor ii.i' ^ -- -builtins: completing for "information_schema.xor" (7,29) +functions: completing for "xor" (7,29) with schema "information_schema" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"information_schema.xor", 7, 29, ""} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 29, "information_schema"} comp at=26 select information_schema. @@ -183,20 +281,28 @@ select information_schema. ii.' ^ -- -builtins: completing for "information_schema." (7,26) +functions: completing for "" (7,26) with schema "information_schema" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"information_schema.", 7, 26, ""} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"", 7, 26, "information_schema"} subtest end @@ -208,30 +314,58 @@ select "pg_catalog".xor ii.i' ^ -- -builtins: completing for "xor" (7,23) +functions: completing for "xor" (7,23) with schema "pg_catalog" --sql: -WITH p AS (SELECT DISTINCT proname FROM pg_catalog.pg_proc) -SELECT $4:::STRING || proname || '(' AS completion, +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, 'functions' AS category, - substr(COALESCE((SELECT details + substr(COALESCE(( + SELECT details FROM "".crdb_internal.builtin_functions f2 - WHERE f2.function = p.proname + WHERE f2.function = p.proname AND f2.schema = p.nspname LIMIT 1), ''), e'[^\n]{0,80}') AS description, $2:::INT AS start, $3:::INT AS end FROM p WHERE left(proname, length($1:::STRING)) = $1:::STRING ---placeholders: []interface {}{"xor", 7, 23, "pg_catalog."} + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 23, "pg_catalog"} # Quoted uppercase is an entire schema entirely. -# Not completing in that case. comp at=22 select "PG_CATALOG".xor ---- ii.i' ^ -- -builtins: not completing +functions: completing for "xor" (7,23) with schema "PG_CATALOG" +--sql: +WITH p AS ( +SELECT DISTINCT + proname, nspname + FROM pg_catalog.pg_proc + JOIN pg_catalog.pg_namespace n ON n.oid = pronamespace) +SELECT IF(length($4) > 0, pg_catalog.quote_ident($4:::STRING) || '.', '') || + pg_catalog.quote_ident(proname) || '(' AS completion, + 'functions' AS category, + substr(COALESCE(( + SELECT details + FROM "".crdb_internal.builtin_functions f2 + WHERE f2.function = p.proname AND f2.schema = p.nspname + LIMIT 1), ''), e'[^\n]{0,80}') AS description, + $2:::INT AS start, + $3:::INT AS end + FROM p + WHERE left(proname, length($1:::STRING)) = $1:::STRING + AND ((length($4) > 0 AND $4 = nspname) + OR (length($4) = 0 AND nspname = ANY current_schemas(true))) +--placeholders: []interface {}{"xor", 7, 23, "PG_CATALOG"} subtest end diff --git a/pkg/sql/crdb_internal.go b/pkg/sql/crdb_internal.go index 7acc6f5add92..a058817240b8 100644 --- a/pkg/sql/crdb_internal.go +++ b/pkg/sql/crdb_internal.go @@ -2721,17 +2721,25 @@ CREATE TABLE crdb_internal.builtin_functions ( function STRING NOT NULL, signature STRING NOT NULL, category STRING NOT NULL, - details STRING NOT NULL + details STRING NOT NULL, + schema STRING NOT NULL )`, populate: func(ctx context.Context, _ *planner, _ catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { for _, name := range builtins.AllBuiltinNames() { props, overloads := builtinsregistry.GetBuiltinProperties(name) + schema := catconstants.PgCatalogName + const crdbInternal = catconstants.CRDBInternalSchemaName + "." + if strings.HasPrefix(name, crdbInternal) { + name = name[len(crdbInternal):] + schema = catconstants.CRDBInternalSchemaName + } for _, f := range overloads { if err := addRow( tree.NewDString(name), tree.NewDString(f.Signature(false /* simplify */)), tree.NewDString(props.Category), tree.NewDString(f.Info), + tree.NewDString(schema), ); err != nil { return err } diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal b/pkg/sql/logictest/testdata/logic_test/crdb_internal index e1c410ff6a3b..da0dc9e514f9 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal @@ -393,10 +393,10 @@ SELECT * FROM crdb_internal.cluster_contention_events WHERE table_id < 0 ---- table_id index_id num_contention_events cumulative_contention_time key txn_id count -query TTTT colnames +query TTTTT colnames SELECT * FROM crdb_internal.builtin_functions WHERE function = '' ---- -function signature category details +function signature category details schema query ITTITTTTTTTBBBB colnames SELECT * FROM crdb_internal.create_statements WHERE database_name = '' diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog b/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog index dcec9c93c1e7..44d5fe16effb 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog @@ -457,7 +457,7 @@ SELECT id, strip_volatile(descriptor) FROM crdb_internal.kv_catalog_descriptor 4294967289 {"table": {"columns": [{"id": 1, "name": "parent_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "parent_schema_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 3, "name": "name", "type": {"family": "StringFamily", "oid": 25}}, {"id": 4, "name": "id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}], "formatVersion": 3, "id": 4294967289, "name": "kv_catalog_namespace", "nextColumnId": 5, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} 4294967290 {"table": {"columns": [{"id": 1, "name": "id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "descriptor", "type": {"family": "JsonFamily", "oid": 3802}}], "formatVersion": 3, "id": 4294967290, "name": "kv_catalog_descriptor", "nextColumnId": 3, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} 4294967291 {"table": {"columns": [{"id": 1, "name": "type", "type": {"family": "StringFamily", "oid": 25}}, {"id": 2, "name": "object_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 3, "name": "sub_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 4, "name": "comment", "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 4294967291, "name": "kv_catalog_comments", "nextColumnId": 5, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} -4294967292 {"table": {"columns": [{"id": 1, "name": "function", "type": {"family": "StringFamily", "oid": 25}}, {"id": 2, "name": "signature", "type": {"family": "StringFamily", "oid": 25}}, {"id": 3, "name": "category", "type": {"family": "StringFamily", "oid": 25}}, {"id": 4, "name": "details", "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 4294967292, "name": "builtin_functions", "nextColumnId": 5, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} +4294967292 {"table": {"columns": [{"id": 1, "name": "function", "type": {"family": "StringFamily", "oid": 25}}, {"id": 2, "name": "signature", "type": {"family": "StringFamily", "oid": 25}}, {"id": 3, "name": "category", "type": {"family": "StringFamily", "oid": 25}}, {"id": 4, "name": "details", "type": {"family": "StringFamily", "oid": 25}}, {"id": 5, "name": "schema", "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 4294967292, "name": "builtin_functions", "nextColumnId": 6, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} 4294967293 {"table": {"columns": [{"id": 1, "name": "node_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "field", "type": {"family": "StringFamily", "oid": 25}}, {"id": 3, "name": "value", "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 4294967293, "name": "node_build_info", "nextColumnId": 4, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} 4294967294 {"table": {"columns": [{"id": 1, "name": "descriptor_id", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "descriptor_name", "type": {"family": "StringFamily", "oid": 25}}, {"id": 3, "name": "index_id", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 4, "name": "column_id", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 5, "name": "dependson_id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 6, "name": "dependson_type", "type": {"family": "StringFamily", "oid": 25}}, {"id": 7, "name": "dependson_index_id", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 8, "name": "dependson_name", "nullable": true, "type": {"family": "StringFamily", "oid": 25}}, {"id": 9, "name": "dependson_details", "nullable": true, "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 4294967294, "name": "backward_dependencies", "nextColumnId": 10, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "primaryIndex": {"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "partitioning": {}, "sharded": {}}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "public"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 4294967295, "version": "1"}} 4294967295 {"schema": {"defaultPrivileges": {"type": "SCHEMA"}, "id": 4294967295, "name": "crdb_internal", "privileges": {"ownerProto": "node", "users": [{"privileges": "512", "userProto": "public"}], "version": 2}, "version": "1"}} diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index 901021acc7e1..8cf136da083b 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -2333,9 +2333,10 @@ func addPgProcBuiltinRow(name string, addRow func(...tree.Datum) error) error { isAggregate := props.Class == tree.AggregateClass isWindow := props.Class == tree.WindowClass nspOid := tree.NewDOid(catconstants.PgCatalogID) - if strings.HasPrefix(name, "crdb_internal.") { + const crdbInternal = catconstants.CRDBInternalSchemaName + "." + if strings.HasPrefix(name, crdbInternal) { nspOid = tree.NewDOid(catconstants.CrdbInternalID) - name = name[len("crdb_internal."):] + name = name[len(crdbInternal):] } for _, builtin := range overloads {