diff --git a/pkg/ccl/backupccl/bench_covering_test.go b/pkg/ccl/backupccl/bench_covering_test.go index 42cf0695a2f4..e9750e21bfb1 100644 --- a/pkg/ccl/backupccl/bench_covering_test.go +++ b/pkg/ccl/backupccl/bench_covering_test.go @@ -39,7 +39,7 @@ func BenchmarkCoverageChecks(b *testing.B) { b.Run(fmt.Sprintf("numFiles=%d", baseFiles), func(b *testing.B) { for _, hasExternalFilesList := range []bool{true, false} { b.Run(fmt.Sprintf("slim=%t", hasExternalFilesList), func(b *testing.B) { - backups, err := MockBackupChain(ctx, numBackups, numSpans, baseFiles, r, hasExternalFilesList, execCfg) + backups, err := MockBackupChain(ctx, numBackups, numSpans, baseFiles, 1<<20, r, hasExternalFilesList, execCfg) require.NoError(b, err) b.ResetTimer() @@ -75,7 +75,7 @@ func BenchmarkRestoreEntryCover(b *testing.B) { for _, hasExternalFilesList := range []bool{true, false} { b.Run(fmt.Sprintf("hasExternalFilesList=%t", hasExternalFilesList), func(b *testing.B) { - backups, err := MockBackupChain(ctx, numBackups, numSpans, baseFiles, r, hasExternalFilesList, execCfg) + backups, err := MockBackupChain(ctx, numBackups, numSpans, baseFiles, 1<<20, r, hasExternalFilesList, execCfg) require.NoError(b, err) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/pkg/ccl/backupccl/restore_span_covering.go b/pkg/ccl/backupccl/restore_span_covering.go index 85bfc94bb1dd..4f1837126b5d 100644 --- a/pkg/ccl/backupccl/restore_span_covering.go +++ b/pkg/ccl/backupccl/restore_span_covering.go @@ -307,6 +307,7 @@ func generateAndSendImportSpans( // lastCovSpanSize is the size of files added to the right-most span of // the cover so far. var lastCovSpanSize int64 + var lastCovSpanCount int var lastCovSpan roachpb.Span var covFilesByLayer [][]*backuppb.BackupManifest_File var firstInSpan bool @@ -390,8 +391,8 @@ func generateAndSendImportSpans( } var filesByLayer [][]*backuppb.BackupManifest_File - var covSize int64 - var newCovFilesSize int64 + var covSize, newCovFilesSize int64 + var covCount, newCovFilesCount int for layer := range newFilesByLayer { for _, file := range newFilesByLayer[layer] { @@ -401,6 +402,7 @@ func generateAndSendImportSpans( } newCovFilesSize += sz } + newCovFilesCount += len(newFilesByLayer[layer]) filesByLayer = append(filesByLayer, newFilesByLayer[layer]) } @@ -413,6 +415,7 @@ func generateAndSendImportSpans( if fsc.overlaps(coverSpan, file.Span) { covSize += sz + covCount++ filesByLayer[layer] = append(filesByLayer[layer], file) } } @@ -422,8 +425,17 @@ func generateAndSendImportSpans( covFilesByLayer = newFilesByLayer lastCovSpan = coverSpan lastCovSpanSize = newCovFilesSize + lastCovSpanCount = newCovFilesCount } else { - if (newCovFilesSize == 0 || lastCovSpanSize+newCovFilesSize <= filter.targetSize) && !firstInSpan { + // We have room to add to the last span if doing so would remain below + // both the target byte size and 200 total files. We limit the number + // of files since we default to running multiple concurrent workers so + // we want to bound sum total open files across all of them to <= 1k. + // We bound the span byte size to improve work distribution and make + // the progress more granular. + fits := lastCovSpanSize+newCovFilesSize <= filter.targetSize && lastCovSpanCount+newCovFilesCount <= 200 + + if (newCovFilesCount == 0 || fits) && !firstInSpan { // If there are no new files that cover this span or if we can add the // files in the new span's cover to the last span's cover and still stay // below targetSize, then we should merge the two spans. @@ -432,6 +444,7 @@ func generateAndSendImportSpans( } lastCovSpan.EndKey = coverSpan.EndKey lastCovSpanSize = lastCovSpanSize + newCovFilesSize + lastCovSpanCount = lastCovSpanCount + newCovFilesCount } else { if err := flush(ctx); err != nil { return err @@ -439,6 +452,7 @@ func generateAndSendImportSpans( lastCovSpan = coverSpan covFilesByLayer = filesByLayer lastCovSpanSize = covSize + lastCovSpanCount = covCount } } firstInSpan = false diff --git a/pkg/ccl/backupccl/restore_span_covering_test.go b/pkg/ccl/backupccl/restore_span_covering_test.go index 0ef4a3327301..41e81a39c90e 100644 --- a/pkg/ccl/backupccl/restore_span_covering_test.go +++ b/pkg/ccl/backupccl/restore_span_covering_test.go @@ -46,7 +46,7 @@ import ( // Files spans are ordered by start key but may overlap. func MockBackupChain( ctx context.Context, - length, spans, baseFiles int, + length, spans, baseFiles, fileSize int, r *rand.Rand, hasExternalFilesList bool, execCfg sql.ExecutorConfig, @@ -108,7 +108,7 @@ func MockBackupChain( backups[i].Files[f].Span.Key = encoding.EncodeVarintAscending(k, int64(start)) backups[i].Files[f].Span.EndKey = encoding.EncodeVarintAscending(k, int64(end)) backups[i].Files[f].Path = fmt.Sprintf("12345-b%d-f%d.sst", i, f) - backups[i].Files[f].EntryCounts.DataSize = 1 << 20 + backups[i].Files[f].EntryCounts.DataSize = int64(fileSize) } es, err := execCfg.DistSQLSrv.ExternalStorageFromURI(ctx, @@ -225,6 +225,9 @@ func checkRestoreCovering( } var spanIdx int for _, c := range cov { + if len(c.Files) > 500 { + return errors.Errorf("%d files in span %v", len(c.Files), c.Span) + } for _, f := range c.Files { if requireSpan, ok := required[f.Path]; ok { requireSpan.Sub(c.Span) @@ -721,8 +724,21 @@ func sanityCheckFileIterator( } } +func TestRestoreEntryCoverTinyFiles(t *testing.T) { + defer leaktest.AfterTest(t)() + runTestRestoreEntryCoverForSpanAndFileCounts(t, 5, 5<<10, []int{5}, []int{1000, 5000}) +} + //lint:ignore U1000 unused func runTestRestoreEntryCover(t *testing.T, numBackups int) { + spans := []int{1, 2, 3, 5, 9, 11, 12} + files := []int{0, 1, 2, 3, 4, 10, 12, 50} + runTestRestoreEntryCoverForSpanAndFileCounts(t, numBackups, 1<<20, spans, files) +} + +func runTestRestoreEntryCoverForSpanAndFileCounts( + t *testing.T, numBackups, fileSize int, spanCounts, fileCounts []int, +) { r, _ := randutil.NewTestRand() ctx := context.Background() tc, _, _, cleanupFn := backupRestoreTestSetup(t, singleNode, 1, InitManualReplication) @@ -752,10 +768,10 @@ func runTestRestoreEntryCover(t *testing.T, numBackups int) { return merged } - for _, spans := range []int{1, 2, 3, 5, 9, 11, 12} { - for _, files := range []int{0, 1, 2, 3, 4, 10, 12, 50} { + for _, spans := range spanCounts { + for _, files := range fileCounts { for _, hasExternalFilesList := range []bool{true, false} { - backups, err := MockBackupChain(ctx, numBackups, spans, files, r, hasExternalFilesList, execCfg) + backups, err := MockBackupChain(ctx, numBackups, spans, files, fileSize, r, hasExternalFilesList, execCfg) require.NoError(t, err) layerToIterFactory, err := backupinfo.GetBackupManifestIterFactories(ctx, execCfg.DistSQLSrv.ExternalStorage, backups, nil, nil) diff --git a/pkg/ccl/backupccl/utils_test.go b/pkg/ccl/backupccl/utils_test.go index fae97bf6f3f0..e0f38a269dc8 100644 --- a/pkg/ccl/backupccl/utils_test.go +++ b/pkg/ccl/backupccl/utils_test.go @@ -44,6 +44,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" "github.com/cockroachdb/cockroach/pkg/util/hlc" @@ -577,6 +578,7 @@ func requireRecoveryEvent( // //lint:ignore U1000 unused func runTestRestoreMemoryMonitoring(t *testing.T, numSplits, numInc, restoreProcessorMaxFiles int) { + skip.WithIssue(t, 119836, "this functionality was never enabled and will likely be removed rather than enabled") const splitSize = 10 numAccounts := numSplits * splitSize var expectedNumFiles int diff --git a/pkg/ccl/logictestccl/testdata/logic_test/udf_params b/pkg/ccl/logictestccl/testdata/logic_test/udf_params index 323cf7ddb37f..12bb220bc2ea 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/udf_params +++ b/pkg/ccl/logictestccl/testdata/logic_test/udf_params @@ -2,6 +2,7 @@ subtest types +# PLpgSQL UDFs statement error pgcode 42P13 pq: function result type must be int because of OUT parameters CREATE FUNCTION f(OUT param INT) RETURNS FLOAT AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL; @@ -15,25 +16,128 @@ statement error pgcode 42P13 pq: function result type must be int because of OUT CREATE FUNCTION f(OUT param INT) RETURNS RECORD AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL; statement ok -CREATE FUNCTION f(OUT param INT) RETURNS INT AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL; +CREATE FUNCTION f(OUT param INT) RETURNS INT AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL; + +query I colnames +SELECT f(); +---- +f +1 + +query I colnames +SELECT * FROM f(); +---- +param +1 statement ok DROP FUNCTION f; +statement error RETURN cannot have a parameter in function with OUT parameters +CREATE FUNCTION f(OUT INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL; + +statement error RETURN cannot have a parameter in function with OUT parameters +CREATE FUNCTION f(OUT INT, OUT INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL; + +statement error RETURN cannot have a parameter in function with OUT parameters +CREATE FUNCTION f(INOUT INT) AS $$ BEGIN RETURN NULL; END $$ LANGUAGE PLpgSQL; + statement ok -CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) RETURNS RECORD AS $$ BEGIN SELECT 1, 2; END $$ LANGUAGE PLpgSQL; +CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) RETURNS RECORD AS $$ +BEGIN + param2 := 2; + RAISE NOTICE '%', param2; +END +$$ LANGUAGE PLpgSQL; + +query T colnames +SELECT f(3); +---- +f +(3,2) + +query T noticetrace +SELECT f(3); +---- +NOTICE: 2 + +# TODO(100405): figure out why this causes an internal error. +# query II colnames +# SELECT * FROM f(3); +# ---- +# param1 param2 +# 3 2 + +# query T noticetrace +# SELECT * FROM f(3); +# ---- +# NOTICE: 2 + +# query I colnames +# SELECT param1 FROM f(3); +# ---- +# param1 +# 3 + +# query I colnames +# SELECT param2 FROM f(3); +# ---- +# param2 +# 2 statement ok DROP FUNCTION f; -# TODO(#100405) Executing this function results in 42601: query has no destination for result data statement ok -CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1, 2, 3; END $$ LANGUAGE PLpgSQL; +CREATE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 3 INTO param1; END $$ LANGUAGE PLpgSQL; + +query II colnames +SELECT * FROM f(1); +---- +param1 param2 +3 NULL + +statement ok +CREATE OR REPLACE FUNCTION f(INOUT param1 INT, OUT param2 INT) AS $$ +BEGIN + RAISE NOTICE '% %', param1, param2; + param1 = 3; + RAISE NOTICE '% %', param1, param2; + SELECT 4 INTO param2; + RAISE NOTICE '% %', param1, param2; +END +$$ LANGUAGE PLpgSQL; + +query T colnames +SELECT f(1); +---- +f +(3,4) + +query T noticetrace +SELECT f(1); +---- +NOTICE: 1 +NOTICE: 3 +NOTICE: 3 4 + +# TODO(100405): figure this out. +# query II colnames +# SELECT * FROM f(1); +# ---- +# param1 param2 +# 3 4 + +# query T noticetrace +# SELECT * FROM f(1); +# ---- +# NOTICE: 1 +# NOTICE: 3 +# NOTICE: 3 4 statement ok DROP FUNCTION f; -# TODO(#100405) Executing this function results in 42601: query has no destination for result data statement ok CREATE FUNCTION f(INOUT param INT) AS $$ BEGIN SELECT 'hello'; END $$ LANGUAGE PLpgSQL; @@ -48,9 +152,54 @@ BEGIN END $$ LANGUAGE PLpgSQL; +query II +SELECT * FROM f(3) +---- +1 2 + +statement ok +DROP FUNCTION f; + +# Verify that function resolution works correctly when dropping functions (OUT +# arguments are ignored). +statement ok +CREATE FUNCTION f(OUT param INT) AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL; + statement ok DROP FUNCTION f; +statement ok +CREATE FUNCTION f(OUT param INT) AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL; + +statement ok +DROP FUNCTION f(OUT INT); + +statement ok +CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO param1; END $$ LANGUAGE PLpgSQL; + +statement ok +DROP FUNCTION f(OUT INT); + +# TODO(119502): uncomment this and invoke the function when $i notation is +# supported. +# statement ok +# CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO $2; END $$ LANGUAGE PLpgSQL; + +statement ok +CREATE FUNCTION f(OUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1 INTO param2; END $$ LANGUAGE PLpgSQL; + +statement error pq: function f\(int\) does not exist +DROP FUNCTION f(INT); + +statement ok +DROP FUNCTION f; + +statement ok +CREATE FUNCTION f(OUT param INT) RETURNS INT AS $$ BEGIN SELECT 1 INTO param; END $$ LANGUAGE PLpgSQL; + +statement ok +DROP FUNCTION f(OUT INT, OUT text, OUT INT); + # Stored Procedures statement ok CREATE PROCEDURE p(OUT param INT) AS $$ BEGIN SELECT 1; END $$ LANGUAGE PLpgSQL; @@ -64,14 +213,12 @@ CREATE PROCEDURE p(IN param1 INT, INOUT param2 INT, OUT param3 INT) AS $$ BEGIN statement ok DROP PROCEDURE p; -# TODO(#100405) Executing this procedure results in 42601: query has no destination for result data statement ok CREATE PROCEDURE p(INOUT param1 INT, OUT param2 INT) AS $$ BEGIN SELECT 1, 2, 3; END $$ LANGUAGE PLpgSQL; statement ok DROP PROCEDURE p; -# TODO(#100405) Executing this procedure results in 42601: query has no destination for result data statement ok CREATE PROCEDURE p(INOUT param INT) AS $$ BEGIN SELECT 'hello'; END $$ LANGUAGE PLpgSQL; @@ -133,3 +280,389 @@ statement ok DROP FUNCTION f_param_types; subtest end + +subtest parameter_names + +# Unlike for SQL UDFs, sharing of parameter names is not allowed across +# different "parameter namespaces" (IN vs OUT). + +statement error pgcode 42P13 pq: parameter name "a" used more than once +CREATE FUNCTION f_same_name(IN a INT, IN a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL; + +statement error pgcode 42P13 pq: parameter name "a" used more than once +CREATE FUNCTION f_same_name(IN a INT, INOUT a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL; + +statement error pgcode 42P13 pq: parameter name "a" used more than once +CREATE FUNCTION f_same_name(OUT a INT, INOUT a INT) RETURNS INT AS $$ BEGIN RETURN a + a; END $$ LANGUAGE PLpgSQL; + +statement error pgcode 42P13 pq: parameter name "a" used more than once +CREATE FUNCTION f_same_name(IN a INT, OUT a INT) RETURNS INT AS $$ BEGIN RETURN a; END $$ LANGUAGE PLpgSQL; + +statement ok +CREATE FUNCTION f_names(IN param_in INT, OUT param_out INT) AS $$ BEGIN SELECT param_in INTO param_out; END $$ LANGUAGE PLpgSQL; + +query I colnames +SELECT f_names(2); +---- +f_names +2 + +query I colnames +SELECT * FROM f_names(2); +---- +param_out +2 + +statement ok +CREATE FUNCTION f_in_int(IN param INT) RETURNS INT AS $$ BEGIN RETURN param; END; $$ LANGUAGE PLpgSQL; + +query I colnames +SELECT f_in_int(2); +---- +f_in_int +2 + +query I colnames +SELECT * FROM f_in_int(2); +---- +f_in_int +2 + +# Changing OUT parameter name is ok. +statement ok +CREATE FUNCTION f_out_int(OUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL; + +statement ok +CREATE OR REPLACE FUNCTION f_out_int(OUT param_new INT) AS $$ BEGIN param_new = 2; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_out_int]; +---- +CREATE FUNCTION public.f_out_int(OUT param_new INT8) + RETURNS INT8 + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param_new := 2; + END; +$$ + +query I colnames +SELECT f_out_int(); +---- +f_out_int +2 + +query I colnames +SELECT * FROM f_out_int(); +---- +param_new +2 + +# But changing IN or INOUT parameter name is not allowed. +statement error pgcode 42P13 pq: cannot change name of input parameter "param" +CREATE OR REPLACE FUNCTION f_in_int(IN param_new INT) RETURNS INT AS $$ BEGIN RETURN param_new; END; $$ LANGUAGE PLpgSQL; + +statement ok +CREATE FUNCTION f_inout_int(INOUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL; + +statement error pgcode 42P13 pq: cannot change name of input parameter "param" +CREATE OR REPLACE FUNCTION f_inout_int(INOUT param_new INT) AS $$ BEGIN param_new = 2; END; $$ LANGUAGE PLpgSQL; + +subtest end + +subtest changing_parameters + +statement ok +CREATE FUNCTION f_int(IN param INT) RETURNS INT AS $$ BEGIN RETURN param; END; $$ LANGUAGE PLpgSQL; + +query I colnames +SELECT * FROM f_int(2); +---- +f_int +2 + +# We can change the parameter class from IN to INOUT without introducing new +# overload. +statement ok +CREATE OR REPLACE FUNCTION f_int(INOUT param INT) AS $$ BEGIN param = 2; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_int]; +---- +CREATE FUNCTION public.f_int(INOUT param INT8) + RETURNS INT8 + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param := 2; + END; +$$ + +query I colnames +SELECT * FROM f_int(2); +---- +param +2 + +# We can add and remove an OUT parameter too without introducing another +# overload (but must preserve the original parameter name for IN / INOUT +# parameter). +statement error pgcode 42P13 pq: cannot change name of input parameter "param" +CREATE OR REPLACE FUNCTION f_int(IN param_in INT, OUT param_out INT) AS $$ BEGIN param_out = param_in; END; $$ LANGUAGE PLpgSQL; + +statement ok +CREATE OR REPLACE FUNCTION f_int(IN param INT, OUT param_out INT) AS $$ BEGIN SELECT param INTO param_out; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_int]; +---- +CREATE FUNCTION public.f_int(IN param INT8, OUT param_out INT8) + RETURNS INT8 + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + SELECT param INTO param_out; + END; +$$ + +query I colnames +SELECT * FROM f_int(2); +---- +param_out +2 + +statement ok +CREATE OR REPLACE FUNCTION f_int(OUT param_out INT, IN param INT) AS $$ BEGIN SELECT param INTO param_out; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_int]; +---- +CREATE FUNCTION public.f_int(OUT param_out INT8, IN param INT8) + RETURNS INT8 + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + SELECT param INTO param_out; + END; +$$ + +query I colnames +SELECT * FROM f_int(2); +---- +param_out +2 + +statement ok +CREATE OR REPLACE FUNCTION f_int(INOUT param INT) AS $$ BEGIN param = param; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_int]; +---- +CREATE FUNCTION public.f_int(INOUT param INT8) + RETURNS INT8 + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param := param; + END; +$$ + +query I colnames +SELECT * FROM f_int(2); +---- +param +2 + +subtest end + +subtest default_parameter_names + +# Parameter names are optional. Whenever a UDF returns RECORD type, each unnamed +# OUT parameter with ordinal 'i' (among all OUT parameters) gets the default +# name that is "column" || i. + +statement ok +CREATE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names]; +---- +CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT INT8) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param2 := 2; + END; +$$ + +query T colnames +SELECT f_default_names(0); +---- +f_default_names +(,2,) + +query III colnames +SELECT * FROM f_default_names(0); +---- +column1 param2 column3 +NULL 2 NULL + +query I colnames +SELECT column1 FROM f_default_names(0); +---- +column1 +NULL + +query I colnames +SELECT param2 FROM f_default_names(0); +---- +param2 +2 + +query I colnames +SELECT column3 FROM f_default_names(0); +---- +column3 +NULL + +# However, attempting to access the parameter by the default names is invalid. +statement error pgcode 42601 pq: \"column1\" is not a known variable +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN SELECT 1 INTO column1; END; $$ LANGUAGE PLpgSQL; + +# Introducing the OUT parameter name is disallowed because it'd change the +# return type. +statement error cannot change return type of existing function +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT param3 INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL; + +# Introducing the name that matches the default OUT parameter name is allowed. +statement ok +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT column3 INT) AS $$ BEGIN SELECT 3 INTO column3; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names]; +---- +CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT column3 INT8) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + SELECT 3 INTO column3; + END; +$$ + +query I colnames +SELECT column3 FROM f_default_names(0); +---- +column3 +3 + +# Then we can omit the default OUT parameter name again (but still cannot use it +# in the body). +statement error pgcode 42601 pq: \"column3\" is not a known variable +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN SELECT 3 INTO column3; END; $$ LANGUAGE PLpgSQL; + +statement ok +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) AS $$ BEGIN param2 = 2; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names]; +---- +CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN INT8, OUT INT8) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param2 := 2; + END; +$$ + +# Introducing the IN parameter name is ok. +statement ok +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN in_param INT, OUT INT) AS $$ BEGIN SELECT in_param INTO param2; END; $$ LANGUAGE PLpgSQL; + +query T +SELECT create_statement FROM [SHOW CREATE FUNCTION f_default_names]; +---- +CREATE FUNCTION public.f_default_names(OUT INT8, OUT param2 INT8, IN in_param INT8, OUT INT8) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + SELECT in_param INTO param2; + END; +$$ + +# But then the IN parameter name cannot be changed anymore. +statement error cannot change name of input parameter "in_param" +CREATE OR REPLACE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN in_param_new INT, OUT INT) AS $$ BEGIN SELECT in_param_new INTO param2; END; $$ LANGUAGE PLpgSQL; + +query T colnames +SELECT f_default_names(0); +---- +f_default_names +(,0,) + +query III colnames +SELECT * FROM f_default_names(0); +---- +column1 param2 column3 +NULL 0 NULL + +subtest end + +statement ok +CREATE TYPE typ AS (a INT, b INT); +CREATE FUNCTION f_udt() RETURNS typ AS $$ BEGIN RETURN (1, 2); END; $$ LANGUAGE PLpgSQL; + +query T colnames +SELECT f_udt() +---- +f_udt +(1,2) + +query II colnames +SELECT * FROM f_udt() +---- +a b +1 2 + +query I colnames +SELECT a FROM f_udt() +---- +a +1 + +query I colnames +SELECT b FROM f_udt() +---- +b +2 diff --git a/pkg/ccl/logictestccl/testdata/logic_test/udf_plpgsql b/pkg/ccl/logictestccl/testdata/logic_test/udf_plpgsql index 1b9cf077fff7..f5ac75744fce 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/udf_plpgsql +++ b/pkg/ccl/logictestccl/testdata/logic_test/udf_plpgsql @@ -2900,3 +2900,11 @@ CREATE OR REPLACE FUNCTION f(x INT) RETURNS INT AS $$ $$ LANGUAGE PLpgSQL; subtest end + +statement ok +CREATE FUNCTION f_empty() RETURNS VOID AS $$ BEGIN END; $$ LANGUAGE PLpgSQL; + +query T +SELECT f_empty() +---- +NULL diff --git a/pkg/ccl/logictestccl/testdata/logic_test/udf_rewrite b/pkg/ccl/logictestccl/testdata/logic_test/udf_rewrite index 2b8b2286176a..9696e10d90eb 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/udf_rewrite +++ b/pkg/ccl/logictestccl/testdata/logic_test/udf_rewrite @@ -388,6 +388,73 @@ ALTER TYPE weekday RENAME VALUE 'humpday' TO 'wednesday'; statement ok DROP FUNCTION f_rewrite(); +statement ok +CREATE FUNCTION f_rewrite(INOUT param1 weekday, OUT param2 weekday) AS +$$ + BEGIN + param2 = param1; + param1 = 'friday'::weekday; + END +$$ LANGUAGE PLPGSQL; + +query T +SELECT get_body_str('f_rewrite'); +---- +"BEGIN\nparam2 := param1;\nparam1 := b'\\xc0':::@100107;\nEND;\n" + +query TT +SHOW CREATE FUNCTION f_rewrite; +---- +f_rewrite CREATE FUNCTION public.f_rewrite(INOUT param1 test.public.weekday, OUT param2 test.public.weekday) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param2 := param1; + param1 := 'friday':::test.public.weekday; + END; + $$ + +statement ok +ALTER TYPE weekday RENAME VALUE 'friday' TO 'humpday'; + +statement ok +ALTER TYPE weekday RENAME TO workday; + +query T +SELECT get_body_str('f_rewrite'); +---- +"BEGIN\nparam2 := param1;\nparam1 := b'\\xc0':::@100107;\nEND;\n" + +query TT +SHOW CREATE FUNCTION f_rewrite; +---- +f_rewrite CREATE FUNCTION public.f_rewrite(INOUT param1 test.public.workday, OUT param2 test.public.workday) + RETURNS RECORD + VOLATILE + NOT LEAKPROOF + CALLED ON NULL INPUT + LANGUAGE plpgsql + AS $$ + BEGIN + param2 := param1; + param1 := 'humpday':::test.public.workday; + END; + $$ + +# Reset types for subtest. +statement ok +ALTER TYPE workday RENAME TO weekday; + +statement ok +ALTER TYPE weekday RENAME VALUE 'humpday' TO 'friday'; + +statement ok +DROP FUNCTION f_rewrite; + subtest end subtest rewrite_proc diff --git a/pkg/cmd/roachtest/testdata/regression.diffs b/pkg/cmd/roachtest/testdata/regression.diffs index b96e2545582a..169b3710c39f 100644 --- a/pkg/cmd/roachtest/testdata/regression.diffs +++ b/pkg/cmd/roachtest/testdata/regression.diffs @@ -60011,10 +60011,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. -+ + +If you would rather not post publicly, please contact us directly +using the support form. - ++ +We appreciate your feedback. + +\df ptest8 @@ -60042,7 +60042,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. -- OUT parameters CREATE PROCEDURE ptest9(OUT a int) LANGUAGE SQL -@@ -219,151 +309,192 @@ +@@ -219,151 +309,188 @@ $$; -- standard way to do a call: CALL ptest9(NULL); @@ -60051,8 +60051,6 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. - 1 -(1 row) - -+ERROR: unknown signature: public.ptest9(unknown) -+HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- you can write an expression, but it's not evaluated CALL ptest9(1/0); -- no error - a @@ -60060,16 +60058,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. - 1 -(1 row) - -+ERROR: unknown signature: public.ptest9(decimal) -+HINT: No function matches the given name and argument types. You might need to add explicit type casts. ++ERROR: ptest9(): unsupported binary operator: / (desired ) -- ... and it had better match the type of the parameter CALL ptest9(1./0.); -- error -ERROR: procedure ptest9(numeric) does not exist -LINE 1: CALL ptest9(1./0.); - ^ -HINT: No procedure matches the given name and argument types. You might need to add explicit type casts. -+ERROR: unknown signature: public.ptest9(decimal) -+HINT: No function matches the given name and argument types. You might need to add explicit type casts. ++ERROR: ptest9(): unsupported binary operator: / (desired ) -- check named-parameter matching CREATE PROCEDURE ptest10(OUT a int, IN b int, IN c int) LANGUAGE SQL AS $$ SELECT b - c $$; @@ -60079,8 +60075,6 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. - 3 -(1 row) - -+ERROR: unknown signature: public.ptest10(unknown, int, int) -+HINT: No function matches the given name and argument types. You might need to add explicit type casts. CALL ptest10(a => null, b => 8, c => 2); - a ---- @@ -60140,6 +60134,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. -- check resolution of ambiguous DROP commands CREATE PROCEDURE ptest10(IN a int, IN b int, IN c int) LANGUAGE SQL AS $$ SELECT a + b - c $$; ++ERROR: function "ptest10" already exists with same argument types \df ptest10 - List of functions - Schema | Name | Result data type | Argument data types | Type @@ -60165,12 +60160,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. +WHERE p.proname OPERATOR(pg_catalog.~) '^(ptest10)$' COLLATE pg_catalog.default + ^ drop procedure ptest10; -- fail - ERROR: procedure name "ptest10" is not unique +-ERROR: procedure name "ptest10" is not unique -HINT: Specify the argument list to select the procedure unambiguously. drop procedure ptest10(int, int, int); -- fail -ERROR: procedure name "ptest10" is not unique ++ERROR: unknown procedure: ptest10() begin; drop procedure ptest10(out int, int, int); ++ERROR: unknown procedure: ptest10() \df ptest10 - List of functions - Schema | Name | Result data type | Argument data types | Type @@ -60199,7 +60196,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. rollback; begin; drop procedure ptest10(in int, int, int); -+ERROR: procedure ptest10(int,int,int) does not exist ++ERROR: unknown procedure: ptest10() \df ptest10 - List of functions - Schema | Name | Result data type | Argument data types | Type @@ -60323,7 +60320,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_procedure. RESET ROLE; GRANT EXECUTE ON PROCEDURE ptest1(text) TO regress_cp_user1; SET ROLE regress_cp_user1; -@@ -371,13 +502,39 @@ +@@ -371,13 +498,39 @@ RESET ROLE; -- ROUTINE syntax ALTER ROUTINE cp_testfunc1(int) RENAME TO cp_testfunc1a; @@ -64930,12 +64927,12 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_view.out - -ERROR: cannot drop constraint view_base_table_pkey on table view_base_table because other objects depend on it -DETAIL: view key_dependent_view depends on constraint view_base_table_pkey on table view_base_table -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "view_base_table" (315): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "view_base_table" (314): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 CREATE VIEW key_dependent_view_no_cols AS SELECT FROM view_base_table GROUP BY key HAVING length(data) > 0; -+ERROR: relation "key_dependent_view_no_cols" (317): table must contain at least 1 column ++ERROR: relation "key_dependent_view_no_cols" (316): table must contain at least 1 column -- -- CREATE OR REPLACE VIEW -- @@ -76835,7 +76832,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/drop_if_exists.ou --- --- role/user/group --- -@@ -68,275 +91,1123 @@ +@@ -68,249 +91,1081 @@ CREATE ROLE regress_test_r1; CREATE GROUP regress_test_g1; DROP USER regress_test_u2; @@ -78014,17 +78011,13 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/drop_if_exists.ou -- cleanup DROP FUNCTION test_ambiguous_funcname(int); DROP FUNCTION test_ambiguous_funcname(text); - -- Likewise for procedures. - CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql; -+ERROR: column "\"\"" does not exist +@@ -319,24 +1174,37 @@ CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql; -+ERROR: column "\"\"" does not exist DROP PROCEDURE test_ambiguous_procname; --ERROR: procedure name "test_ambiguous_procname" is not unique + ERROR: procedure name "test_ambiguous_procname" is not unique -HINT: Specify the argument list to select the procedure unambiguously. -+ERROR: unknown procedure: test_ambiguous_procname() DROP PROCEDURE IF EXISTS test_ambiguous_procname; --ERROR: procedure name "test_ambiguous_procname" is not unique + ERROR: procedure name "test_ambiguous_procname" is not unique -HINT: Specify the argument list to select the procedure unambiguously. -- Check we get a similar error if we use ROUTINE instead of PROCEDURE. DROP ROUTINE IF EXISTS test_ambiguous_procname; @@ -78037,9 +78030,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/drop_if_exists.ou +HINT: try \h DROP -- cleanup DROP PROCEDURE test_ambiguous_procname(int); -+ERROR: unknown procedure: test_ambiguous_procname() DROP PROCEDURE test_ambiguous_procname(text); -+ERROR: unknown procedure: test_ambiguous_procname() -- This test checks both the functionality of 'if exists' and the syntax -- of the drop database command. drop database test_database_exists (force); @@ -79559,7 +79550,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s -- check display of function arguments in sub-SELECT CREATE TABLE functest1 (i int); CREATE FUNCTION functest_S_16(a int, b int) RETURNS void -@@ -286,220 +420,208 @@ +@@ -286,220 +420,206 @@ BEGIN ATOMIC INSERT INTO functest1 SELECT a + $2; END; @@ -79825,9 +79816,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s RETURNS int LANGUAGE SQL AS 'SELECT $1'; -+ERROR: unimplemented: default value -+HINT: You have attempted to use a feature that is not yet implemented. -+See: https://go.crdb.dev/issue-v/100962/v24.1 ++ERROR: no value provided for placeholder: $1 CREATE FUNCTION functest_IS_3(a int default 1, out b int) RETURNS int LANGUAGE SQL @@ -79905,7 +79894,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s SELECT r0.routine_name, r1.routine_name FROM information_schema.routine_routine_usage rru JOIN information_schema.routines r0 ON r0.specific_name = rru.specific_name -@@ -507,61 +629,63 @@ +@@ -507,61 +627,63 @@ WHERE r0.routine_schema = 'temp_func_test' AND r1.routine_schema = 'temp_func_test' ORDER BY 1, 2; @@ -80001,7 +79990,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s DROP FUNCTION functest1(a int); -- inlining of set-returning functions CREATE TABLE functest3 (a int); -@@ -581,35 +705,38 @@ +@@ -581,35 +703,38 @@ (3 rows) EXPLAIN (verbose, costs off) SELECT * FROM functest_sri1(); @@ -80060,7 +80049,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s -- Check behavior of VOID-returning SQL functions CREATE FUNCTION voidtest1(a int) RETURNS VOID LANGUAGE SQL AS $$ SELECT a + 1 $$; -@@ -621,20 +748,17 @@ +@@ -621,20 +746,17 @@ CREATE FUNCTION voidtest2(a int, b int) RETURNS VOID LANGUAGE SQL AS $$ SELECT voidtest1(a + b) $$; @@ -80089,7 +80078,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s CREATE TEMP TABLE sometable(f1 int); CREATE FUNCTION voidtest3(a int) RETURNS VOID LANGUAGE SQL AS $$ INSERT INTO sometable VALUES(a + 1) $$; -@@ -664,7 +788,8 @@ +@@ -664,7 +786,8 @@ SELECT * FROM voidtest5(3); voidtest5 ----------- @@ -80099,7 +80088,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_function_s -- Regression tests for bugs: -- Check that arguments that are R/W expanded datums aren't corrupted by -@@ -674,70 +799,60 @@ +@@ -674,70 +797,60 @@ CREATE FUNCTION double_append(anyarray, anyelement) RETURNS SETOF anyarray LANGUAGE SQL IMMUTABLE AS $$ SELECT array_append($1, $2) || array_append($1, $2) $$; @@ -90459,7 +90448,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/triggers.out --la -- including that the WHEN clause works create function bark(text) returns bool language plpgsql immutable as $$ begin raise notice '% <- woof!', $1; return true; end; $$; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 create or replace function trigger_notice_ab() returns trigger as $$ begin raise notice 'trigger % on % % % for %: (a,b)=(%,%)', @@ -111971,15 +111960,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- -- test that the aggregate transition logic correctly handles -- transition / combine functions returning NULL -- First test the case of a normal transition function returning NULL -@@ -2701,6 +2437,7 @@ - END IF; - RETURN NULL; - END$$; -+ERROR: duplicate declaration at or near """" - CREATE AGGREGATE balk(int4) - ( - SFUNC = balkifnull(int8, int4), -@@ -2708,12 +2445,14 @@ +@@ -2708,12 +2444,14 @@ PARALLEL = SAFE, INITCOND = '0' ); @@ -111999,7 +111980,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- ROLLBACK; -- Secondly test the case of a parallel aggregate combiner function -- returning NULL. For that use normal transition function, but a -@@ -2730,6 +2469,23 @@ +@@ -2730,6 +2468,23 @@ END IF; RETURN NULL; END$$; @@ -112023,7 +112004,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- CREATE AGGREGATE balk(int4) ( SFUNC = int4_sum(int8, int4), -@@ -2738,26 +2494,27 @@ +@@ -2738,26 +2493,27 @@ PARALLEL = SAFE, INITCOND = '0' ); @@ -112065,7 +112046,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- ROLLBACK; -- test multiple usage of an aggregate whose finalfn returns a R/W datum BEGIN; -@@ -2767,6 +2524,8 @@ +@@ -2767,6 +2523,8 @@ RETURN array_fill(y[1], ARRAY[4]); END; $$; @@ -112074,7 +112055,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- CREATE FUNCTION rwagg_finalfunc(x anyarray) RETURNS anyarray LANGUAGE plpgsql STRICT IMMUTABLE AS $$ DECLARE -@@ -2777,11 +2536,18 @@ +@@ -2777,11 +2535,18 @@ RETURN res; END; $$; @@ -112093,7 +112074,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- CREATE FUNCTION eatarray(x real[]) RETURNS real[] LANGUAGE plpgsql STRICT IMMUTABLE AS $$ BEGIN -@@ -2789,21 +2555,35 @@ +@@ -2789,21 +2554,35 @@ RETURN x; END; $$; @@ -112134,7 +112115,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- -- variance(int4) covers numeric_poly_combine -- sum(int8) covers int8_avg_combine -- regr_count(float8, float8) covers int8inc_float8_float8 and aggregates with > 1 arg -@@ -2813,36 +2593,17 @@ +@@ -2813,36 +2592,17 @@ UNION ALL SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk1) u; @@ -112177,7 +112158,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- -- variance(int8) covers numeric_combine -- avg(numeric) covers numeric_avg_combine EXPLAIN (COSTS OFF, VERBOSE) -@@ -2851,46 +2612,22 @@ +@@ -2851,46 +2611,22 @@ UNION ALL SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk1) u; @@ -112232,7 +112213,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- -- Ensure that the STRICT checks for aggregates does not take NULLness -- of ORDER BY columns into account. See bug report around -- 2a505161-2727-2473-7c46-591ed108ac52@email.cz -@@ -2929,27 +2666,46 @@ +@@ -2929,27 +2665,46 @@ -- does not lead to array overflow due to unexpected duplicate hash keys -- see CAFeeJoKKu0u+A_A9R9316djW-YW3-+Gtgvy3ju655qRHR3jtdA@mail.gmail.com set enable_memoize to off; @@ -112291,7 +112272,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- select unique1, count(*), sum(twothousand) from tenk1 group by unique1 having sum(fivethous) > 4975 -@@ -3007,12 +2763,48 @@ +@@ -3007,12 +2762,48 @@ (48 rows) set work_mem to default; @@ -112340,7 +112321,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- create table agg_data_2k as select g from generate_series(0, 1999) g; analyze agg_data_2k; -@@ -3021,19 +2813,28 @@ +@@ -3021,19 +2812,28 @@ analyze agg_data_20k; -- Produce results with sorting. set enable_hashagg = false; @@ -112378,7 +112359,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- create table agg_group_1 as select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 from agg_data_20k group by g%10000; -@@ -3048,6 +2849,7 @@ +@@ -3048,6 +2848,7 @@ where g < r.a group by g/2) as s; set jit_above_cost to default; @@ -112386,7 +112367,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- create table agg_group_3 as select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 from agg_data_2k group by g/2; -@@ -3056,18 +2858,41 @@ +@@ -3056,18 +2857,41 @@ from agg_data_2k group by g/2; -- Produce results with hash aggregation set enable_hashagg = true; @@ -112435,7 +112416,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- create table agg_hash_1 as select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 from agg_data_20k group by g%10000; -@@ -3082,6 +2907,7 @@ +@@ -3082,6 +2906,7 @@ where g < r.a group by g/2) as s; set jit_above_cost to default; @@ -112443,7 +112424,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/aggregates.out -- create table agg_hash_3 as select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 from agg_data_2k group by g/2; -@@ -3089,7 +2915,31 @@ +@@ -3089,7 +2914,31 @@ select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 from agg_data_2k group by g/2; set enable_sort = true; @@ -131010,10 +130991,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/generated.out --l -(0 rows) + attrelid | attname | attgenerated +------------+--------------------------+-------------- -+ 3931264561 | crdb_internal_idx_expr | v ++ 2586187552 | crdb_internal_idx_expr | v + 4046462210 | crdb_internal_idx_expr | v + 4046462212 | crdb_internal_idx_expr_1 | v -+ 1135731663 | crdb_internal_idx_expr | v ++ 875995384 | crdb_internal_idx_expr | v +(4 rows) CREATE TABLE gtest0 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (55) STORED); @@ -143251,7 +143232,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/privileges.out -- CREATE FUNCTION leak2(integer,integer) RETURNS boolean AS $$begin raise notice 'leak % %', $1, $2; return $1 > $2; end$$ LANGUAGE plpgsql immutable; -+ERROR: duplicate declaration at or near """" ++ERROR: no value provided for placeholder: $1 CREATE OPERATOR >>> (procedure = leak2, leftarg = integer, rightarg = integer, restrict = scalargtsel); +ERROR: at or near ">": syntax error: unimplemented: this syntax @@ -158400,7 +158381,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/merge.out --label -- remove constraints alter table target drop CONSTRAINT target_pkey; -+ERROR: relation "target" (1236): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "target" (1237): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 alter table target alter column tid drop not null; @@ -160561,7 +160542,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_table_like CREATE TABLE inhf (LIKE inhx, LIKE inhx); /* Throw error */ -ERROR: column "xx" specified more than once -+ERROR: relation "inhf" (1266): duplicate column name: "xx" ++ERROR: relation "inhf" (1267): duplicate column name: "xx" CREATE TABLE inhf (LIKE inhx INCLUDING DEFAULTS INCLUDING CONSTRAINTS); INSERT INTO inhf DEFAULT VALUES; SELECT * FROM inhf; /* Single entry with value 'text' */ @@ -161360,7 +161341,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/create_table_like diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/collate.linux.utf8_1.out --label=/mnt/data1/postgres/src/test/regress/results/collate.linux.utf8.out /mnt/data1/postgres/src/test/regress/expected/collate.linux.utf8_1.out /mnt/data1/postgres/src/test/regress/results/collate.linux.utf8.out --- /mnt/data1/postgres/src/test/regress/expected/collate.linux.utf8_1.out +++ /mnt/data1/postgres/src/test/regress/results/collate.linux.utf8.out -@@ -7,5 +7,923 @@ +@@ -7,5 +7,922 @@ (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE', 'tr_TR') AND collencoding = pg_char_to_encoding('UTF8')) <> 4 OR version() !~ 'linux-gnu' AS skip_test \gset @@ -161860,7 +161841,6 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/collate.linux.utf + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; -+ERROR: duplicate declaration at or near """" +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b @@ -162287,7 +162267,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/collate.linux.utf diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/collate.windows.win1252_1.out --label=/mnt/data1/postgres/src/test/regress/results/collate.windows.win1252.out /mnt/data1/postgres/src/test/regress/expected/collate.windows.win1252_1.out /mnt/data1/postgres/src/test/regress/results/collate.windows.win1252.out --- /mnt/data1/postgres/src/test/regress/expected/collate.windows.win1252_1.out +++ /mnt/data1/postgres/src/test/regress/results/collate.windows.win1252.out -@@ -9,5 +9,813 @@ +@@ -9,5 +9,812 @@ (SELECT count(*) FROM pg_collation WHERE collname IN ('de_DE', 'en_US', 'sv_SE') AND collencoding = pg_char_to_encoding('WIN1252')) <> 3 OR (version() !~ 'Visual C\+\+' AND version() !~ 'mingw32' AND version() !~ 'windows') AS skip_test \gset @@ -162680,7 +162660,6 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/collate.windows.w + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; -+ERROR: duplicate declaration at or near """" +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b @@ -173132,7 +173111,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/stats_ext.out --l CREATE FUNCTION op_leak(int, int) RETURNS bool AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END' LANGUAGE plpgsql; -+ERROR: duplicate declaration at or near """" ++ERROR: no value provided for placeholder: $1 CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int, restrict = scalarltsel); +ERROR: at or near "<": syntax error: unimplemented: this syntax @@ -183830,7 +183809,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/functional_deps.o -ERROR: cannot drop constraint articles_pkey on table articles because other objects depend on it -DETAIL: view fdv1 depends on constraint articles_pkey on table articles -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "articles" (1516): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles" (1519): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 DROP VIEW fdv1; @@ -183843,14 +183822,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/functional_deps.o -ERROR: cannot drop constraint articles_pkey on table articles because other objects depend on it -DETAIL: view fdv2 depends on constraint articles_pkey on table articles -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "articles" (1516): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles" (1519): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 ALTER TABLE articles_in_category DROP CONSTRAINT articles_in_category_pkey RESTRICT; --fail -ERROR: cannot drop constraint articles_in_category_pkey on table articles_in_category because other objects depend on it -DETAIL: view fdv2 depends on constraint articles_in_category_pkey on table articles_in_category -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "articles_in_category" (1517): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles_in_category" (1520): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 DROP VIEW fdv2; @@ -183863,7 +183842,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/functional_deps.o -ERROR: cannot drop constraint articles_pkey on table articles because other objects depend on it -DETAIL: view fdv3 depends on constraint articles_pkey on table articles -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "articles" (1516): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles" (1519): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 DROP VIEW fdv3; @@ -183873,7 +183852,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/functional_deps.o -ERROR: cannot drop constraint articles_pkey on table articles because other objects depend on it -DETAIL: view fdv4 depends on constraint articles_pkey on table articles -HINT: Use DROP ... CASCADE to drop the dependent objects too. -+ERROR: relation "articles" (1516): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles" (1519): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 DROP VIEW fdv4; @@ -183883,7 +183862,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/functional_deps.o (0 rows) ALTER TABLE articles DROP CONSTRAINT articles_pkey RESTRICT; -+ERROR: relation "articles" (1516): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "articles" (1519): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 EXECUTE foo; -- fail @@ -224292,7 +224271,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- sql, proretset = t, prorettype = record CREATE FUNCTION getrngfunc7(int) RETURNS setof record AS 'SELECT * FROM rngfunc WHERE rngfuncid = $1;' LANGUAGE SQL; SELECT * FROM getrngfunc7(1) AS t1(rngfuncid int, rngfuncsubid int, rngfuncname text); -@@ -544,143 +445,119 @@ +@@ -544,143 +445,126 @@ (2 rows) SELECT * FROM ROWS FROM( getrngfunc7(1) AS (rngfuncid int, rngfuncsubid int, rngfuncname text) ) WITH ORDINALITY; @@ -224346,21 +224325,20 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- +ERROR: relation "vw_getrngfunc" does not exist -- plpgsql, proretset = f, prorettype = b CREATE FUNCTION getrngfunc8(int) RETURNS int AS 'DECLARE rngfuncint int; BEGIN SELECT rngfuncid into rngfuncint FROM rngfunc WHERE rngfuncid = $1; RETURN rngfuncint; END;' LANGUAGE plpgsql; -+ERROR: column "\"\"" does not exist SELECT * FROM getrngfunc8(1) AS t1; -- t1 ------ + t1 + ---- - 1 --(1 row) -- -+ERROR: unknown function: getrngfunc8() ++ + (1 row) + SELECT * FROM getrngfunc8(1) WITH ORDINALITY AS t1(v,o); -- v | o -----+--- + v | o + ---+--- - 1 | 1 --(1 row) -- -+ERROR: unknown function: getrngfunc8() ++ | 1 + (1 row) + CREATE VIEW vw_getrngfunc AS SELECT * FROM getrngfunc8(1); +ERROR: unknown function: getrngfunc8() +HINT: There is probably a typo in function name. Or the intention was to use a user-defined function in the view query, which is currently not supported. @@ -224502,17 +224480,15 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- DROP FUNCTION getrngfunc1(int); DROP FUNCTION getrngfunc2(int); DROP FUNCTION getrngfunc3(int); -@@ -689,7 +566,9 @@ - DROP FUNCTION getrngfunc6(int); +@@ -690,6 +574,7 @@ DROP FUNCTION getrngfunc7(int); DROP FUNCTION getrngfunc8(int); -+ERROR: unknown function: getrngfunc8() DROP FUNCTION getrngfunc9(int); +ERROR: unknown function: getrngfunc9() DROP FUNCTION rngfunct(int); DROP TABLE rngfunc2; DROP TABLE rngfunc; -@@ -700,6 +579,9 @@ +@@ -700,6 +585,9 @@ CREATE FUNCTION rngfunc_sql(int,int) RETURNS setof rngfunc_rescan_t AS 'SELECT i, nextval(''rngfunc_rescan_seq1'') FROM generate_series($1,$2) i;' LANGUAGE SQL; -- plpgsql functions use materialize mode CREATE FUNCTION rngfunc_mat(int,int) RETURNS setof rngfunc_rescan_t AS 'begin for i in $1..$2 loop return next (i, nextval(''rngfunc_rescan_seq2'')); end loop; end;' LANGUAGE plpgsql; @@ -224522,7 +224498,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- --invokes ExecReScanFunctionScan - all these cases should materialize the function only once -- LEFT JOIN on a condition that the planner can't prove to be true is used to ensure the function -- is on the inner path of a nestloop join -@@ -750,19 +632,7 @@ +@@ -750,19 +638,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) ON (r+i)<100; @@ -224543,7 +224519,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -770,19 +640,7 @@ +@@ -770,19 +646,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN rngfunc_mat(11,13) WITH ORDINALITY AS f(i,s,o) ON (r+i)<100; @@ -224564,7 +224540,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -790,30 +648,18 @@ +@@ -790,30 +654,18 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r) LEFT JOIN ROWS FROM( rngfunc_sql(11,13), rngfunc_mat(11,13) ) WITH ORDINALITY AS f(i1,s1,i2,s2,o) ON (r+i1+i2)<100; @@ -224600,7 +224576,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- 3 | 13 (9 rows) -@@ -821,13 +667,13 @@ +@@ -821,13 +673,13 @@ r | i | o ---+----+--- 1 | 11 | 1 @@ -224618,7 +224594,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- 3 | 13 | 3 (9 rows) -@@ -977,16 +823,7 @@ +@@ -977,16 +829,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13); @@ -224636,7 +224612,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -994,16 +831,7 @@ +@@ -994,16 +837,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(10+r,13) WITH ORDINALITY AS f(i,s,o); @@ -224654,7 +224630,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1011,16 +839,7 @@ +@@ -1011,16 +845,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r); @@ -224672,7 +224648,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1028,16 +847,7 @@ +@@ -1028,16 +853,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), rngfunc_mat(11,10+r) WITH ORDINALITY AS f(i,s,o); @@ -224690,7 +224666,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1045,20 +855,7 @@ +@@ -1045,20 +861,7 @@ (1 row) SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2); @@ -224712,7 +224688,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1066,20 +863,7 @@ +@@ -1066,20 +869,7 @@ (1 row) SELECT * FROM (VALUES (11,12),(13,15),(16,20)) v(r1,r2), rngfunc_mat(r1,r2) WITH ORDINALITY AS f(i,s,o); @@ -224734,7 +224710,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- selective rescan of multiple functions: SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval -@@ -1088,16 +872,7 @@ +@@ -1088,16 +878,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(11,11), rngfunc_mat(10+r,13) ); @@ -224752,7 +224728,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1105,16 +880,7 @@ +@@ -1105,16 +886,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(11,11) ); @@ -224770,7 +224746,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1122,16 +888,7 @@ +@@ -1122,16 +894,7 @@ (1 row) SELECT * FROM (VALUES (1),(2),(3)) v(r), ROWS FROM( rngfunc_sql(10+r,13), rngfunc_mat(10+r,13) ); @@ -224788,7 +224764,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT setval('rngfunc_rescan_seq1',1,false),setval('rngfunc_rescan_seq2',1,false); setval | setval --------+-------- -@@ -1139,23 +896,7 @@ +@@ -1139,23 +902,7 @@ (1 row) SELECT * FROM generate_series(1,2) r1, generate_series(r1,3) r2, ROWS FROM( rngfunc_sql(10+r1,13), rngfunc_mat(10+r2,13) ); @@ -224813,7 +224789,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT * FROM (VALUES (1),(2),(3)) v(r), generate_series(10+r,20-r) f(i); r | i ---+---- -@@ -1243,31 +984,31 @@ +@@ -1243,31 +990,31 @@ r1 | r1 | r2 | i ----+----+----+---- 1 | 1 | 10 | 21 @@ -224863,7 +224839,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- 3 | 3 | 30 | 23 (27 rows) -@@ -1302,95 +1043,47 @@ +@@ -1302,95 +1049,47 @@ r1 | r1 | r2 | i ----+----+----+---- 1 | 1 | 10 | 10 @@ -224982,7 +224958,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- check handling of FULL JOIN with multiple lateral references (bug #15741) SELECT * FROM (VALUES (1),(2)) v1(r1) -@@ -1404,20 +1097,10 @@ +@@ -1404,20 +1103,10 @@ ) AS ss1 ON TRUE FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE ) AS ss0 ON TRUE; @@ -225005,7 +224981,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- DROP SEQUENCE rngfunc_rescan_seq1; DROP SEQUENCE rngfunc_rescan_seq2; -- -@@ -1449,7 +1132,7 @@ +@@ -1449,7 +1138,7 @@ -- error, wrong result type CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int) RETURNS float AS 'select $1+1' LANGUAGE sql; @@ -225014,7 +224990,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- with multiple OUT params you must get a RECORD result CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS int AS 'select $1+1' LANGUAGE sql; -@@ -1457,8 +1140,8 @@ +@@ -1457,8 +1146,8 @@ CREATE OR REPLACE FUNCTION rngfunc(in f1 int, out f2 int, out f3 text) RETURNS record AS 'select $1+1' LANGUAGE sql; @@ -225025,7 +225001,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- CREATE OR REPLACE FUNCTION rngfuncr(in f1 int, out f2 int, out text) AS $$select $1-1, $1::text || 'z'$$ LANGUAGE sql; SELECT f1, rngfuncr(f1) FROM int4_tbl; -@@ -1478,23 +1161,11 @@ +@@ -1478,23 +1167,11 @@ (1 row) SELECT * FROM rngfuncr(42) AS p(a,b); @@ -225051,7 +225027,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- SELECT * FROM rngfuncb(42, 99); f2 | column2 ----+--------- -@@ -1502,11 +1173,7 @@ +@@ -1502,11 +1179,7 @@ (1 row) SELECT * FROM rngfuncb(42, 99) AS p(a,b); @@ -225064,7 +225040,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- Can reference function with or without OUT params for DROP, etc DROP FUNCTION rngfunc(int); DROP FUNCTION rngfuncr(in f2 int, out f1 int, out text); -@@ -1523,24 +1190,23 @@ +@@ -1523,24 +1196,23 @@ (1 row) SELECT dup('xyz'); -- fails @@ -225095,7 +225071,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- DROP FUNCTION dup(anyelement); -- equivalent behavior, though different name exposed for input arg CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray) -@@ -1555,133 +1221,108 @@ +@@ -1555,133 +1227,108 @@ -- fails, no way to deduce outputs CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; @@ -225275,7 +225251,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (2 rows) -- insert will execute to completion even if function needs just 1 row -@@ -1691,16 +1332,16 @@ +@@ -1691,16 +1338,16 @@ select insert_tt('fool'); insert_tt ----------- @@ -225297,7 +225273,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (4 rows) -- setof does what's expected -@@ -1710,50 +1351,50 @@ +@@ -1710,50 +1357,50 @@ select insert_tt2('foolish','barrish'); insert_tt2 ------------ @@ -225371,7 +225347,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (10 rows) -- triggers will fire, too -@@ -1762,72 +1403,88 @@ +@@ -1762,72 +1409,88 @@ raise notice 'noticetrigger % %', new.f1, new.data; return null; end $$ language plpgsql; @@ -225497,7 +225473,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- test case for a whole-row-variable bug create function rngfunc1(n integer, out a text, out b text) -@@ -1835,6 +1492,18 @@ +@@ -1835,6 +1498,18 @@ language sql as $$ select 'foo ' || i, 'bar ' || i from generate_series(1,$1) i $$; set work_mem='64kB'; @@ -225516,7 +225492,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- select t.a, t, t.a from rngfunc1(10000) t limit 1; a | t | a -------+-------------------+------- -@@ -1842,6 +1511,18 @@ +@@ -1842,6 +1517,18 @@ (1 row) reset work_mem; @@ -225535,7 +225511,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- select t.a, t, t.a from rngfunc1(10000) t limit 1; a | t | a -------+-------------------+------- -@@ -1871,31 +1552,24 @@ +@@ -1871,31 +1558,24 @@ select * from array_to_set(array['one', 'two']); -- fail ERROR: a column definition list is required for functions returning "record" @@ -225579,7 +225555,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- but without, it can be: create or replace function array_to_set(anyarray) returns setof record as $$ select i AS "index", $1[i] AS "value" from generate_subscripts($1, 1) i -@@ -1914,26 +1588,21 @@ +@@ -1914,26 +1594,21 @@ 2 | two (2 rows) @@ -225618,7 +225594,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- create temp table rngfunc(f1 int8, f2 int8); create function testrngfunc() returns record as $$ insert into rngfunc values (1,2) returning *; -@@ -1952,8 +1621,6 @@ +@@ -1952,8 +1627,6 @@ select * from testrngfunc(); -- fail ERROR: a column definition list is required for functions returning "record" @@ -225627,7 +225603,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- drop function testrngfunc(); create function testrngfunc() returns setof record as $$ insert into rngfunc values (1,2), (3,4) returning *; -@@ -1974,8 +1641,6 @@ +@@ -1974,8 +1647,6 @@ select * from testrngfunc(); -- fail ERROR: a column definition list is required for functions returning "record" @@ -225636,7 +225612,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- drop function testrngfunc(); -- Check that typmod imposed by a composite type is honored create type rngfunc_type as (f1 numeric(35,6), f2 numeric(35,2)); -@@ -1984,31 +1649,28 @@ +@@ -1984,31 +1655,28 @@ $$ language sql immutable; explain (verbose, costs off) select testrngfunc(); @@ -225684,7 +225660,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (1 row) create or replace function testrngfunc() returns rngfunc_type as $$ -@@ -2016,31 +1678,28 @@ +@@ -2016,31 +1684,28 @@ $$ language sql volatile; explain (verbose, costs off) select testrngfunc(); @@ -225732,7 +225708,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (1 row) drop function testrngfunc(); -@@ -2049,31 +1708,28 @@ +@@ -2049,31 +1714,28 @@ $$ language sql immutable; explain (verbose, costs off) select testrngfunc(); @@ -225780,7 +225756,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (1 row) create or replace function testrngfunc() returns setof rngfunc_type as $$ -@@ -2081,93 +1737,72 @@ +@@ -2081,93 +1743,72 @@ $$ language sql volatile; explain (verbose, costs off) select testrngfunc(); @@ -225916,7 +225892,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- -- Check some cases involving added/dropped columns in a rowtype result -- -@@ -2178,79 +1813,37 @@ +@@ -2178,79 +1819,37 @@ create or replace function get_first_user() returns users as $$ SELECT * FROM users ORDER BY userid LIMIT 1; $$ language sql stable; @@ -226009,7 +225985,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- We used to have a bug that would allow the above to succeed, posing -- hazards for later execution of the view. Check that the internal -- defenses for those hazards haven't bit-rotted, in case some other -@@ -2264,18 +1857,17 @@ +@@ -2264,18 +1863,17 @@ returning pg_describe_object(classid, objid, objsubid) as obj, pg_describe_object(refclassid, refobjid, refobjsubid) as ref, deptype; @@ -226035,7 +226011,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- -- likewise, check we don't crash if the dependency goes wrong begin; -- destroy the dependency entry that prevents the ALTER: -@@ -2286,19 +1878,18 @@ +@@ -2286,19 +1884,18 @@ returning pg_describe_object(classid, objid, objsubid) as obj, pg_describe_object(refclassid, refobjid, refobjsubid) as ref, deptype; @@ -226061,7 +226037,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- drop table users; -- check behavior with type coercion required for a set-op create or replace function rngfuncbar() returns setof text as -@@ -2320,17 +1911,11 @@ +@@ -2320,17 +1917,11 @@ -- this function is now inlinable, too: explain (verbose, costs off) select * from rngfuncbar(); @@ -226084,7 +226060,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- drop function rngfuncbar(); -- check handling of a SQL function with multiple OUT params (bug #5777) create or replace function rngfuncbar(out integer, out numeric) as -@@ -2344,115 +1929,182 @@ +@@ -2344,115 +1935,182 @@ create or replace function rngfuncbar(out integer, out numeric) as $$ select (1, 2) $$ language sql; select * from rngfuncbar(); -- fail @@ -226340,7 +226316,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/rangefuncs.out -- (0 rows) drop type rngfunc2; -@@ -2463,25 +2115,16 @@ +@@ -2463,25 +2121,16 @@ from unnest(array['{"lectures": [{"id": "1"}]}'::jsonb]) as unnested_modules(module)) as ss, jsonb_to_recordset(ss.lecture) as j (id text); @@ -228839,7 +228815,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/alter_table.out - ALTER TABLE attmp3 VALIDATE CONSTRAINT b_le_20; -- succeeds -- An already validated constraint must not be revalidated CREATE FUNCTION boo(int) RETURNS int IMMUTABLE STRICT LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'boo: %', $1; RETURN $1; END; $$; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 INSERT INTO attmp7 VALUES (8, 18); +ERROR: relation "attmp7" does not exist ALTER TABLE attmp7 ADD CONSTRAINT identity CHECK (b = boo(b)); @@ -229668,7 +229644,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/alter_table.out - -ERROR: column "test" is in a primary key +ERROR: column "test" is in a primary index alter table atacc1 drop constraint "atacc1_pkey"; -+ERROR: relation "atacc1" (1876): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "atacc1" (1880): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 alter table atacc1 alter column test drop not null; @@ -233231,13 +233207,13 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/alter_table.out - +ERROR: index with name "tt9_c_key" already exists ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE(c); -- fail, dup name -ERROR: constraint "foo" for relation "tt9" already exists -+ERROR: error executing StatementPhase stage 1 of 1 with 7 MutationType ops: relation "tt9" (1970): duplicate constraint name: "foo" ++ERROR: error executing StatementPhase stage 1 of 1 with 7 MutationType ops: relation "tt9" (1974): duplicate constraint name: "foo" ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK(c > 5); -- fail, dup name -ERROR: constraint "tt9_c_key" for relation "tt9" already exists -+ERROR: error executing StatementPhase stage 1 of 1 with 2 MutationType ops: relation "tt9" (1970): duplicate constraint name: "tt9_c_key" ++ERROR: error executing StatementPhase stage 1 of 1 with 2 MutationType ops: relation "tt9" (1974): duplicate constraint name: "tt9_c_key" ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK(c > 6); ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name -+ERROR: error executing StatementPhase stage 1 of 1 with 7 MutationType ops: relation "tt9" (1970): duplicate constraint name: "tt9_c_key2" ++ERROR: error executing StatementPhase stage 1 of 1 with 7 MutationType ops: relation "tt9" (1974): duplicate constraint name: "tt9_c_key2" \d tt9 - Table "public.tt9" - Column | Type | Collation | Nullable | Default @@ -233415,7 +233391,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/alter_table.out - DELETE FROM old_system_table WHERE othercol = 'somedata'; TRUNCATE old_system_table; ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey; -+ERROR: relation "old_system_table" (1973): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction ++ERROR: relation "old_system_table" (1977): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction +HINT: You have attempted to use a feature that is not yet implemented. +See: https://go.crdb.dev/issue-v/48026/v24.1 ALTER TABLE old_system_table DROP COLUMN othercol; @@ -237068,7 +237044,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out raise notice 'bleat %', $1; return $1; end$$ language plpgsql; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 create function sql_if(bool, anyelement, anyelement) returns anyelement as $$ select case when $1 then $2 else $3 end $$ language sql; -- Note this would fail with integer overflow, never mind wrong bleat() output, @@ -237648,7 +237624,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out (1 row) select pg_typeof(array[1.2,55.5]); -- numeric[] -@@ -1122,252 +1455,226 @@ +@@ -1122,252 +1455,224 @@ (1 row) select pg_typeof(myleast(10, 1, 20, 33)); -- polymorphic input @@ -237713,9 +237689,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out create function dfunc(a int = 1, out sum int, b int = 2) as $$ select $1 + $2; $$ language sql; -+ERROR: unimplemented: default value -+HINT: You have attempted to use a feature that is not yet implemented. -+See: https://go.crdb.dev/issue-v/100962/v24.1 ++ERROR: no value provided for placeholder: $2 select dfunc(); - dfunc -------- @@ -238026,7 +238000,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out -- but this works since the ambiguous functions aren't preferred anyway select dfunc('Hi'); dfunc -@@ -1376,7 +1683,9 @@ +@@ -1376,7 +1681,9 @@ (1 row) drop function dfunc(int, int, int); @@ -238036,7 +238010,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out drop function dfunc(text); -- -- Tests for named- and mixed-notation function calling -@@ -1385,185 +1694,159 @@ +@@ -1385,185 +1692,156 @@ returns table (a int, b int, c int, d int) as $$ select $1, $2, $3, $4; $$ language sql; @@ -238233,8 +238207,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out returns record as $$ select $1, $2; $$ language sql; -+ERROR: return type mismatch in function declared to return record -+DETAIL: Final statement returns varchar instead of decimal at column 2 ++ERROR: no value provided for placeholder: $2 select (dfunc()).*; - _a | _c --------+---- @@ -238310,22 +238283,20 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out $$ language sql; -ERROR: cannot change name of input parameter "c" -HINT: Use DROP FUNCTION dfunc(character varying,numeric) first. -+ERROR: return type mismatch in function declared to return record -+DETAIL: Final statement returns varchar instead of decimal at column 2 ++ERROR: no value provided for placeholder: $2 create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric) returns record as $$ select $1, $2; $$ language sql; -ERROR: cannot change name of input parameter "c" -HINT: Use DROP FUNCTION dfunc(character varying,numeric) first. -+ERROR: return type mismatch in function declared to return record -+DETAIL: Final statement returns varchar instead of decimal at column 2 ++ERROR: no value provided for placeholder: $2 drop function dfunc(varchar, numeric); +ERROR: unknown function: dfunc() --fail, named parameters are not unique create function testpolym(a int, a int) returns int as $$ select 1;$$ language sql; ERROR: parameter name "a" used more than once -@@ -1583,189 +1866,149 @@ +@@ -1583,189 +1861,149 @@ drop function testpolym(int); create function testpolym(a int) returns table(a int) as $$ select $1;$$ language sql; @@ -238620,7 +238591,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out -- need DO to protect the -- from psql do $$ declare r integer; -@@ -1775,40 +2018,38 @@ +@@ -1775,40 +2013,38 @@ raise info 'r = %', r; end; $$; @@ -238683,7 +238654,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/polymorphism.out -- -- Tests for ANYCOMPATIBLE polymorphism family -- -@@ -1816,283 +2057,213 @@ +@@ -1816,283 +2052,213 @@ returns anycompatible as $$ select greatest($1, $2) $$ language sql; @@ -245181,11 +245152,12 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- The following tests are unrelated to the scenario outlined above; -- they merely exercise specific parts of PL/pgSQL -@@ -1564,12 +1866,9 @@ +@@ -1564,12 +1866,10 @@ END IF; RETURN rslt; END;' LANGUAGE plpgsql; -+ERROR: duplicate declaration at or near """" ++ERROR: unknown function: recursion_test() ++HINT: There is probably a typo in function name. Or the intention was to use a user-defined function in the function body, which is currently not supported. SELECT recursion_test(4,3); - recursion_test ----------------- @@ -245196,7 +245168,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test the FOUND magic variable -- -@@ -1609,22 +1908,44 @@ +@@ -1609,22 +1909,44 @@ end if; return true; end;' language plpgsql; @@ -245227,14 +245199,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + for i in 1 .. 10 loop + ^ +HINT: You have attempted to use a feature that is not yet implemented. - ++ +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. + +If you would rather not post publicly, please contact us directly +using the support form. -+ + +We appreciate your feedback. + +select test_found(); @@ -245255,7 +245227,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test set-returning functions for PL/pgSQL -@@ -1638,17 +1959,11 @@ +@@ -1638,17 +1960,11 @@ END LOOP; RETURN; END;' language plpgsql; @@ -245277,7 +245249,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function test_table_func_row() returns setof found_test_tbl as ' DECLARE row found_test_tbl%ROWTYPE; -@@ -1658,17 +1973,11 @@ +@@ -1658,17 +1974,11 @@ END LOOP; RETURN; END;' language plpgsql; @@ -245299,7 +245271,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function test_ret_set_scalar(int,int) returns setof int as ' DECLARE i int; -@@ -1678,21 +1987,11 @@ +@@ -1678,21 +1988,11 @@ END LOOP; RETURN; END;' language plpgsql; @@ -245325,7 +245297,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function test_ret_set_rec_dyn(int) returns setof record as ' DECLARE retval RECORD; -@@ -1708,20 +2007,21 @@ +@@ -1708,20 +2008,21 @@ END IF; RETURN; END;' language plpgsql; @@ -245359,7 +245331,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function test_ret_rec_dyn(int) returns record as ' DECLARE retval RECORD; -@@ -1734,18 +2034,21 @@ +@@ -1734,18 +2035,21 @@ RETURN retval; END IF; END;' language plpgsql; @@ -245391,7 +245363,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test some simple polymorphism cases. -- -@@ -1753,43 +2056,45 @@ +@@ -1753,43 +2057,45 @@ begin return x + 1; end$$ language plpgsql; @@ -245459,7 +245431,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop function f1(x anyarray); create function f1(x anyarray) returns anyarray as $$ begin -@@ -1802,72 +2107,61 @@ +@@ -1802,72 +2108,61 @@ (1 row) select f1(stavalues1) from pg_statistic; -- fail, can't infer element type @@ -245551,7 +245523,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function f1(a anyelement, b anyarray, c anycompatible, d anycompatible, OUT x anyarray, OUT y anycompatiblearray) -@@ -1876,35 +2170,30 @@ +@@ -1876,35 +2171,30 @@ x := a || b; y := array[c, d]; end$$ language plpgsql; @@ -245601,11 +245573,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test handling of OUT parameters, including polymorphic cases. -- Note that RETURN is optional with OUT params; we try both ways. -@@ -1914,14 +2203,17 @@ - begin +@@ -1915,25 +2205,21 @@ return i+1; end$$ language plpgsql; --ERROR: RETURN cannot have a parameter in function with OUT parameters + ERROR: RETURN cannot have a parameter in function with OUT parameters -LINE 3: return i+1; - ^ create function f1(in i int, out j int) as $$ @@ -245619,30 +245590,24 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + j := i+1; + return; + ^ - select f1(42); - f1 - ---- -@@ -1939,17 +2231,9 @@ - i := i+1; - end$$ language plpgsql; select f1(42); - f1 ----- - 43 -(1 row) - -+ERROR: control reached end of function without RETURN ++ERROR: unknown function: f1() select * from f1(42); -- i +- j ----- - 43 -(1 row) - -+ERROR: control reached end of function without RETURN - drop function f1(int); - create function f1(in i int, out j int) returns setof int as $$ ++ERROR: unknown function: f1() + create or replace function f1(inout i int) as $$ begin -@@ -1959,14 +2243,27 @@ + i := i+1; +@@ -1959,14 +2245,27 @@ return next; return; end$$ language plpgsql; @@ -245663,12 +245628,12 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. - ++ +If you would rather not post publicly, please contact us directly +using the support form. + +We appreciate your feedback. -+ + +select * from f1(42); +ERROR: unknown function: f1() drop function f1(int); @@ -245676,27 +245641,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function f1(in i int, out j int, out k text) as $$ begin j := i; -@@ -1974,17 +2271,9 @@ - k := 'foo'; - end$$ language plpgsql; - select f1(42); -- f1 ------------ -- (43,foo) --(1 row) -- -+ERROR: "j" is not a known variable - select * from f1(42); -- j | k ------+----- -- 43 | foo --(1 row) -- -+ERROR: "j" is not a known variable - drop function f1(int); - create function f1(in i int, out j int, out k text) returns setof record as $$ - begin -@@ -1995,52 +2284,60 @@ +@@ -1995,52 +2294,60 @@ k := 'foot'; return next; end$$ language plpgsql; @@ -245783,15 +245728,15 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- test PERFORM -- -@@ -2057,6 +2354,7 @@ +@@ -2057,6 +2364,7 @@ RETURN FALSE; END IF; END;' language plpgsql; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 create function perform_test_func() returns void as ' BEGIN IF FOUND then -@@ -2077,19 +2375,32 @@ +@@ -2077,19 +2385,32 @@ RETURN; END;' language plpgsql; @@ -245810,11 +245755,11 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + PERFORM perform_simple_func(5); + ^ +HINT: You have attempted to use a feature that is not yet implemented. -+ + +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. - ++ +If you would rather not post publicly, please contact us directly +using the support form. + @@ -245835,7 +245780,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop table perform_test; -- -@@ -2103,19 +2414,12 @@ +@@ -2103,19 +2424,12 @@ if found then return x; end if; return 0; end$$ language plpgsql stable; @@ -245858,7 +245803,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function sp_add_user(a_login text) returns int as $$ declare my_id_user int; begin -@@ -2130,38 +2434,22 @@ +@@ -2130,38 +2444,22 @@ END IF; RETURN my_id_user; end$$ language plpgsql; @@ -245906,7 +245851,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- tests for refcursors -- -@@ -2185,12 +2473,13 @@ +@@ -2185,12 +2483,13 @@ return x.a; end $$ language plpgsql; @@ -245925,7 +245870,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function return_refcursor(rc refcursor) returns refcursor as $$ begin open rc for select a from rc_test; -@@ -2203,33 +2492,31 @@ +@@ -2203,33 +2502,31 @@ return $1; end $$ language plpgsql; @@ -245979,7 +245924,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab commit; -- should fail fetch next from test1; -@@ -2249,13 +2536,25 @@ +@@ -2249,13 +2546,25 @@ end if; end $$ language plpgsql; @@ -246010,7 +245955,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- should fail create function constant_refcursor() returns refcursor as $$ declare -@@ -2266,8 +2565,11 @@ +@@ -2266,8 +2575,11 @@ end $$ language plpgsql; select constant_refcursor(); @@ -246024,7 +245969,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- but it's okay like this create or replace function constant_refcursor() returns refcursor as $$ declare -@@ -2301,13 +2603,25 @@ +@@ -2301,13 +2613,25 @@ end if; end $$ language plpgsql; @@ -246055,7 +246000,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- mixing named and positional argument notations create function namedparmcursor_test2(int, int) returns boolean as $$ declare -@@ -2324,12 +2638,24 @@ +@@ -2324,12 +2648,24 @@ end if; end $$ language plpgsql; @@ -246077,15 +246022,15 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. - -+We appreciate your feedback. + ++We appreciate your feedback. + +select namedparmcursor_test2(20, 20); +ERROR: unknown function: namedparmcursor_test2() -- mixing named and positional: param2 is given twice, once in named notation -- and second time in positional notation. Should throw an error at parse time create function namedparmcursor_test3() returns void as $$ -@@ -2339,9 +2665,22 @@ +@@ -2339,9 +2675,22 @@ open c1(param2 := 20, 21); end $$ language plpgsql; @@ -246111,7 +246056,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- mixing named and positional: same as previous test, but param1 is duplicated create function namedparmcursor_test4() returns void as $$ declare -@@ -2350,9 +2689,22 @@ +@@ -2350,9 +2699,22 @@ open c1(20, param1 := 21); end $$ language plpgsql; @@ -246137,7 +246082,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- duplicate named parameter, should throw an error at parse time create function namedparmcursor_test5() returns void as $$ declare -@@ -2362,9 +2714,22 @@ +@@ -2362,9 +2724,22 @@ open c1 (p2 := 77, p2 := 42); end $$ language plpgsql; @@ -246163,7 +246108,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- not enough parameters, should throw an error at parse time create function namedparmcursor_test6() returns void as $$ declare -@@ -2374,9 +2739,22 @@ +@@ -2374,9 +2749,22 @@ open c1 (p2 := 77); end $$ language plpgsql; @@ -246189,7 +246134,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- division by zero runtime error, the context given in the error message -- should be sensible create function namedparmcursor_test7() returns void as $$ -@@ -2386,10 +2764,24 @@ +@@ -2386,10 +2774,24 @@ begin open c1 (p2 := 77, p1 := 42/0); end $$ language plpgsql; @@ -246217,7 +246162,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- check that line comments work correctly within the argument list (there -- is some special handling of this case in the code: the newline after the -- comment must be preserved when the argument-evaluating query is -@@ -2406,12 +2798,24 @@ +@@ -2406,12 +2808,24 @@ fetch c1 into n; return n; end $$ language plpgsql; @@ -246239,15 +246184,15 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select namedparmcursor_test8(); +ERROR: unknown function: namedparmcursor_test8() -- cursor parameter name can match plpgsql variable or unreserved keyword create function namedparmcursor_test9(p1 int) returns int4 as $$ declare -@@ -2425,12 +2829,24 @@ +@@ -2425,12 +2839,24 @@ fetch c1 into n; return n; end $$ language plpgsql; @@ -246269,15 +246214,15 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select namedparmcursor_test9(6); +ERROR: unknown function: namedparmcursor_test9() -- -- tests for "raise" processing -- -@@ -2441,7 +2857,6 @@ +@@ -2441,28 +2867,22 @@ end; $$ language plpgsql; ERROR: too many parameters specified for RAISE @@ -246285,18 +246230,19 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function raise_test2(int) returns int as $$ begin raise notice 'This message has too few parameters: %, %, %', $1, $1; -@@ -2449,20 +2864,15 @@ + return $1; end; $$ language plpgsql; - ERROR: too few parameters specified for RAISE +-ERROR: too few parameters specified for RAISE -CONTEXT: compilation of PL/pgSQL function "raise_test2" near line 3 ++ERROR: no value provided for placeholder: $1 create function raise_test3(int) returns int as $$ begin raise notice 'This message has no parameters (despite having %% signs in it)!'; return $1; end; $$ language plpgsql; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 select raise_test3(1); -NOTICE: This message has no parameters (despite having % signs in it)! - raise_test3 @@ -246308,7 +246254,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Test re-RAISE inside a nested exception block. This case is allowed -- by Oracle's PL/SQL but was handled differently by PG before 9.1. CREATE FUNCTION reraise_test() RETURNS void AS $$ -@@ -2484,14 +2894,30 @@ +@@ -2484,14 +2904,30 @@ raise notice 'WRONG - exception % caught in outer block', sqlerrm; END; $$ LANGUAGE plpgsql; @@ -246331,14 +246277,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + RAISE; + ^ +HINT: You have attempted to use a feature that is not yet implemented. -+ + +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. + +If you would rather not post publicly, please contact us directly +using the support form. - ++ +We appreciate your feedback. + +SELECT reraise_test(); @@ -246346,7 +246292,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- reject function definitions that contain malformed SQL queries at -- compile-time, where possible -@@ -2504,9 +2930,17 @@ +@@ -2504,9 +2940,17 @@ a := 10; return a; end$$ language plpgsql; @@ -246367,7 +246313,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function bad_sql2() returns int as $$ declare r record; begin -@@ -2515,81 +2949,117 @@ +@@ -2515,81 +2959,117 @@ end loop; return 5; end;$$ language plpgsql; @@ -246432,10 +246378,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. - ++ +If you would rather not post publicly, please contact us directly +using the support form. -+ + +We appreciate your feedback. + +-- select void_return_expr(); @@ -246528,7 +246474,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- SQLSTATE and SQLERRM test -- -@@ -2597,14 +3067,11 @@ +@@ -2597,14 +3077,11 @@ begin raise notice '% %', sqlstate, sqlerrm; end; $$ language plpgsql; @@ -246545,7 +246491,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function excpt_test2() returns void as $$ begin begin -@@ -2613,13 +3080,10 @@ +@@ -2613,13 +3090,10 @@ end; end; end; $$ language plpgsql; @@ -246561,7 +246507,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function excpt_test3() returns void as $$ begin begin -@@ -2639,31 +3103,61 @@ +@@ -2639,31 +3113,61 @@ raise notice '% %', sqlstate, sqlerrm; end; end; $$ language plpgsql; @@ -246593,9 +246539,9 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select excpt_test3(); +ERROR: unknown function: excpt_test3() create function excpt_test4() returns text as $$ @@ -246621,9 +246567,9 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select excpt_test4(); +ERROR: unknown function: excpt_test4() drop function excpt_test1(); @@ -246637,7 +246583,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- parameters of raise stmt can be expressions create function raise_exprs() returns void as $$ declare -@@ -2714,13 +3208,11 @@ +@@ -2714,13 +3218,11 @@ insert into foo values(5,6) returning * into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246655,7 +246601,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2728,10 +3220,11 @@ +@@ -2728,10 +3230,11 @@ insert into foo values(7,8),(9,10) returning * into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246670,7 +246616,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2739,13 +3232,11 @@ +@@ -2739,13 +3242,11 @@ execute 'insert into foo values(5,6) returning *' into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246688,7 +246634,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2753,23 +3244,17 @@ +@@ -2753,23 +3254,17 @@ execute 'insert into foo values(7,8),(9,10) returning *' into x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246717,7 +246663,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; -@@ -2778,13 +3263,11 @@ +@@ -2778,13 +3273,11 @@ select * from foo where f1 = 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246735,7 +246681,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2792,9 +3275,11 @@ +@@ -2792,9 +3285,11 @@ select * from foo where f1 = 0 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246749,7 +246695,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2802,10 +3287,11 @@ +@@ -2802,10 +3297,11 @@ select * from foo where f1 > 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246764,7 +246710,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2813,13 +3299,11 @@ +@@ -2813,13 +3309,11 @@ execute 'select * from foo where f1 = 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246782,7 +246728,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2827,9 +3311,11 @@ +@@ -2827,9 +3321,11 @@ execute 'select * from foo where f1 = 0' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246796,7 +246742,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2837,12 +3323,17 @@ +@@ -2837,12 +3333,17 @@ execute 'select * from foo where f1 > 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246816,7 +246762,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; -@@ -2853,10 +3344,11 @@ +@@ -2853,10 +3354,11 @@ select * from foo where f1 = p1 and f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246831,7 +246777,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; -@@ -2867,10 +3359,11 @@ +@@ -2867,10 +3369,11 @@ select * from foo where f1 = p1 and f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246846,7 +246792,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; -@@ -2881,11 +3374,11 @@ +@@ -2881,11 +3384,11 @@ select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246862,7 +246808,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2893,10 +3386,11 @@ +@@ -2893,10 +3396,11 @@ select * from foo where f1 > 3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246877,7 +246823,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2904,10 +3398,11 @@ +@@ -2904,10 +3408,11 @@ execute 'select * from foo where f1 = $1 or f1::text = $2' using 0, 'foo' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246892,7 +246838,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2915,10 +3410,11 @@ +@@ -2915,10 +3420,11 @@ execute 'select * from foo where f1 > $1' using 1 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246907,7 +246853,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ declare x record; begin -@@ -2926,9 +3422,11 @@ +@@ -2926,9 +3432,11 @@ execute 'select * from foo where f1 > 3' into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246921,7 +246867,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stricttest() returns void as $$ -- override the global #print_strict_params off -@@ -2941,10 +3439,12 @@ +@@ -2941,10 +3449,12 @@ select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246937,7 +246883,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab reset plpgsql.print_strict_params; create or replace function stricttest() returns void as $$ -- override the global -@@ -2958,11 +3458,12 @@ +@@ -2958,11 +3468,12 @@ select * from foo where f1 > p1 or f1::text = p3 into strict x; raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2; end$$ language plpgsql; @@ -246954,7 +246900,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test warnings and errors set plpgsql.extra_warnings to 'all'; set plpgsql.extra_warnings to 'none'; -@@ -2979,23 +3480,18 @@ +@@ -2979,23 +3490,18 @@ begin end $$ language plpgsql; @@ -246987,7 +246933,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function shadowtest(in1 int) returns table (out1 int) as $$ declare -@@ -3004,18 +3500,17 @@ +@@ -3004,18 +3510,17 @@ begin end $$ language plpgsql; @@ -247015,7 +246961,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- shadowing in a second DECLARE block create or replace function shadowtest() returns void as $$ -@@ -3027,10 +3522,13 @@ +@@ -3027,10 +3532,13 @@ begin end; end$$ language plpgsql; @@ -247032,7 +246978,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- several levels of shadowing create or replace function shadowtest(in1 int) returns void as $$ -@@ -3042,13 +3540,13 @@ +@@ -3042,13 +3550,13 @@ begin end; end$$ language plpgsql; @@ -247052,7 +246998,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- shadowing in cursor definitions create or replace function shadowtest() returns void as $$ -@@ -3057,34 +3555,49 @@ +@@ -3057,34 +3565,49 @@ c1 cursor (f1 int) for select 1; begin end$$ language plpgsql; @@ -247117,7 +247063,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- runtime extra checks set plpgsql.extra_warnings to 'too_many_rows'; do $$ -@@ -3093,8 +3606,10 @@ +@@ -3093,8 +3616,10 @@ select v from generate_series(1,2) g(v) into x; end; $$; @@ -247130,7 +247076,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab set plpgsql.extra_errors to 'too_many_rows'; do $$ declare x int; -@@ -3102,9 +3617,10 @@ +@@ -3102,9 +3627,10 @@ select v from generate_series(1,2) g(v) into x; end; $$; @@ -247144,7 +247090,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab reset plpgsql.extra_errors; reset plpgsql.extra_warnings; set plpgsql.extra_warnings to 'strict_multi_assignment'; -@@ -3118,12 +3634,10 @@ +@@ -3118,12 +3644,10 @@ select 1,2,3 into x, y; end $$; @@ -247161,7 +247107,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab set plpgsql.extra_errors to 'strict_multi_assignment'; do $$ declare -@@ -3135,10 +3649,10 @@ +@@ -3135,10 +3659,10 @@ select 1,2,3 into x, y; end $$; @@ -247176,7 +247122,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create table test_01(a int, b int, c int); alter table test_01 drop column a; -- the check is active only when source table is not empty -@@ -3153,11 +3667,10 @@ +@@ -3153,11 +3677,10 @@ select * from test_01 into x; -- should to fail end; $$; @@ -247192,7 +247138,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare t test_01; -@@ -3167,11 +3680,10 @@ +@@ -3167,11 +3690,10 @@ select 1, 2, 3 into t; -- should fail; end; $$; @@ -247208,7 +247154,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare t test_01; -@@ -3179,10 +3691,10 @@ +@@ -3179,10 +3701,10 @@ select 1 into t; -- should fail; end; $$; @@ -247223,7 +247169,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop table test_01; reset plpgsql.extra_errors; reset plpgsql.extra_warnings; -@@ -3201,16 +3713,11 @@ +@@ -3201,16 +3723,11 @@ close c; end; $$ language plpgsql; @@ -247244,7 +247190,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c no scroll cursor for select f1 from int4_tbl; -@@ -3225,10 +3732,11 @@ +@@ -3225,10 +3742,11 @@ close c; end; $$ language plpgsql; @@ -247259,7 +247205,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c refcursor; -@@ -3243,16 +3751,11 @@ +@@ -3243,16 +3761,11 @@ close c; end; $$ language plpgsql; @@ -247280,7 +247226,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c refcursor; -@@ -3267,14 +3770,11 @@ +@@ -3267,14 +3780,11 @@ close c; end; $$ language plpgsql; @@ -247299,7 +247245,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c refcursor; -@@ -3290,13 +3790,11 @@ +@@ -3290,13 +3800,11 @@ close c; end; $$ language plpgsql; @@ -247317,7 +247263,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c cursor for select * from generate_series(1, 10); -@@ -3316,14 +3814,11 @@ +@@ -3316,14 +3824,11 @@ close c; end; $$ language plpgsql; @@ -247336,7 +247282,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function sc_test() returns setof integer as $$ declare c cursor for select * from generate_series(1, 10); -@@ -3338,13 +3833,13 @@ +@@ -3338,13 +3843,13 @@ close c; end; $$ language plpgsql; @@ -247355,7 +247301,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test qualified variable names create function pl_qual_names (param1 int) returns void as $$ <> -@@ -3362,17 +3857,22 @@ +@@ -3362,17 +3867,22 @@ end; end; $$ language plpgsql; @@ -247377,9 +247323,9 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select pl_qual_names(42); +ERROR: unknown function: pl_qual_names() drop function pl_qual_names(int); @@ -247387,7 +247333,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- tests for RETURN QUERY create function ret_query1(out int, out int) returns setof record as $$ begin -@@ -3383,24 +3883,13 @@ +@@ -3383,24 +3893,13 @@ return next; end; $$ language plpgsql; @@ -247418,7 +247364,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create type record_type as (x text, y int, z boolean); create or replace function ret_query2(lim int) returns setof record_type as $$ begin -@@ -3408,20 +3897,11 @@ +@@ -3408,20 +3907,11 @@ from generate_series(-8, lim) s (x) where s.x % 2 = 0; end; $$ language plpgsql; @@ -247443,7 +247389,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test EXECUTE USING create function exc_using(int, text) returns int as $$ declare i int; -@@ -3433,19 +3913,27 @@ +@@ -3433,19 +3923,27 @@ return i; end $$ language plpgsql; @@ -247482,7 +247428,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function exc_using(int) returns void as $$ declare c refcursor; -@@ -3461,19 +3949,29 @@ +@@ -3461,19 +3959,29 @@ return; end; $$ language plpgsql; @@ -247513,9 +247459,9 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select exc_using(5); +ERROR: unknown function: exc_using() drop function exc_using(int); @@ -247523,7 +247469,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test FOR-over-cursor create or replace function forc01() returns void as $$ declare -@@ -3513,29 +4011,24 @@ +@@ -3513,29 +4021,24 @@ return; end; $$ language plpgsql; @@ -247559,10 +247505,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. -+ + +If you would rather not post publicly, please contact us directly +using the support form. - ++ +We appreciate your feedback. + +select forc01(); @@ -247570,7 +247516,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- try updating the cursor's current row create temp table forc_test as select n as i, n as j from generate_series(1,10) n; -@@ -3549,35 +4042,39 @@ +@@ -3549,35 +4052,39 @@ end loop; end; $$ language plpgsql; @@ -247597,14 +247543,14 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + for r in c loop + ^ +HINT: You have attempted to use a feature that is not yet implemented. - ++ +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. + +If you would rather not post publicly, please contact us directly +using the support form. -+ + +We appreciate your feedback. + +select forc01(); @@ -247637,7 +247583,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab (10 rows) -- same, with a cursor whose portal name doesn't match variable name -@@ -3595,38 +4092,42 @@ +@@ -3595,38 +4102,42 @@ end loop; end; $$ language plpgsql; @@ -247707,7 +247653,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- it's okay to re-use a cursor variable name, even when bound do $$ declare cnt int := 0; -@@ -3642,7 +4143,10 @@ +@@ -3642,7 +4153,10 @@ end loop; raise notice 'cnt = %', cnt; end $$; @@ -247719,7 +247665,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- fail because cursor has no query bound to it create or replace function forc_bad() returns void as $$ declare -@@ -3653,9 +4157,24 @@ +@@ -3653,9 +4167,24 @@ end loop; end; $$ language plpgsql; @@ -247747,7 +247693,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test RETURN QUERY EXECUTE create or replace function return_dquery() returns setof int as $$ -@@ -3664,16 +4183,13 @@ +@@ -3664,16 +4193,13 @@ return query execute 'select * from (values($1),($2)) f' using 40,50; end; $$ language plpgsql; @@ -247769,7 +247715,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test RETURN QUERY with dropped columns create table tabwithcols(a int, b int, c int, d int); insert into tabwithcols values(10,20,30,40),(50,60,70,80); -@@ -3684,46 +4200,22 @@ +@@ -3684,46 +4210,22 @@ return query execute 'select * from tabwithcols'; end; $$ language plpgsql; @@ -247824,7 +247770,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop table tabwithcols; -- -- Tests for composite-type results -@@ -3753,6 +4245,9 @@ +@@ -3753,6 +4255,9 @@ return v; end; $$ language plpgsql; @@ -247834,7 +247780,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab select compos(); compos ----------- -@@ -3778,9 +4273,11 @@ +@@ -3778,9 +4283,11 @@ end; $$ language plpgsql; select compos(); @@ -247849,7 +247795,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- ... but this does create or replace function compos() returns compostype as $$ begin -@@ -3803,12 +4300,11 @@ +@@ -3803,12 +4310,11 @@ return v; end; $$ language plpgsql; @@ -247866,7 +247812,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test: return row expr in return statement. create or replace function composrec() returns record as $$ begin -@@ -3833,26 +4329,22 @@ +@@ -3833,26 +4339,22 @@ return next (2, 'goodbye')::compostype; end; $$ language plpgsql; @@ -247900,7 +247846,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- RETURN variable is a different code path ... create or replace function compos() returns compostype as $$ declare x int := 42; -@@ -3861,8 +4353,7 @@ +@@ -3861,8 +4363,7 @@ end; $$ language plpgsql; select * from compos(); @@ -247910,7 +247856,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop function compos(); -- test: invalid use of composite variable in scalar-returning function create or replace function compos() returns int as $$ -@@ -3874,8 +4365,7 @@ +@@ -3874,8 +4375,7 @@ end; $$ language plpgsql; select compos(); @@ -247920,7 +247866,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test: invalid use of composite expression in scalar-returning function create or replace function compos() returns int as $$ begin -@@ -3883,8 +4373,7 @@ +@@ -3883,8 +4383,7 @@ end; $$ language plpgsql; select compos(); @@ -247930,7 +247876,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop function compos(); drop type compostype; -- -@@ -3904,7 +4393,6 @@ +@@ -3904,7 +4403,6 @@ HINT: some hint ERROR: 1 2 3 DETAIL: some detail info @@ -247938,7 +247884,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Since we can't actually see the thrown SQLSTATE in default psql output, -- test it like this; this also tests re-RAISE create or replace function raise_test() returns void as $$ -@@ -3917,11 +4405,33 @@ +@@ -3917,11 +4415,33 @@ raise; end; $$ language plpgsql; @@ -247975,7 +247921,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise 'check me' -@@ -3932,11 +4442,33 @@ +@@ -3932,11 +4452,33 @@ raise; end; $$ language plpgsql; @@ -248012,7 +247958,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- SQLSTATE specification in WHEN create or replace function raise_test() returns void as $$ begin -@@ -3948,11 +4480,33 @@ +@@ -3948,11 +4490,33 @@ raise; end; $$ language plpgsql; @@ -248049,7 +247995,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise division_by_zero using detail = 'some detail info'; -@@ -3962,11 +4516,32 @@ +@@ -3962,11 +4526,32 @@ raise; end; $$ language plpgsql; @@ -248085,7 +248031,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise division_by_zero; -@@ -3974,7 +4549,6 @@ +@@ -3974,7 +4559,6 @@ $$ language plpgsql; select raise_test(); ERROR: division_by_zero @@ -248093,7 +248039,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise sqlstate '1234F'; -@@ -3982,7 +4556,6 @@ +@@ -3982,7 +4566,6 @@ $$ language plpgsql; select raise_test(); ERROR: 1234F @@ -248101,7 +248047,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise division_by_zero using message = 'custom' || ' message'; -@@ -3990,7 +4563,6 @@ +@@ -3990,7 +4573,6 @@ $$ language plpgsql; select raise_test(); ERROR: custom message @@ -248109,7 +248055,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise using message = 'custom' || ' message', errcode = '22012'; -@@ -3998,34 +4570,48 @@ +@@ -3998,34 +4580,48 @@ $$ language plpgsql; select raise_test(); ERROR: custom message @@ -248165,7 +248111,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test access to exception data create function zero_divide() returns int as $$ declare v int := 0; -@@ -4033,6 +4619,7 @@ +@@ -4033,6 +4629,7 @@ return 10 / v; end; $$ language plpgsql; @@ -248173,7 +248119,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function raise_test() returns void as $$ begin raise exception 'custom exception' -@@ -4055,13 +4642,27 @@ +@@ -4055,13 +4652,27 @@ _sqlstate, _message, replace(_context, E'\n', ' <- '); end; $$ language plpgsql; @@ -248196,10 +248142,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. -+ + +If you would rather not post publicly, please contact us directly +using the support form. - ++ +We appreciate your feedback. + +select stacked_diagnostics_test(); @@ -248207,7 +248153,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function stacked_diagnostics_test() returns void as $$ declare _detail text; _hint text; -@@ -4076,13 +4677,27 @@ +@@ -4076,13 +4687,27 @@ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; end; $$ language plpgsql; @@ -248230,18 +248176,18 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. -+ + +If you would rather not post publicly, please contact us directly +using the support form. + +We appreciate your feedback. - ++ +select stacked_diagnostics_test(); +ERROR: unknown function: stacked_diagnostics_test() -- fail, cannot use stacked diagnostics statement outside handler create or replace function stacked_diagnostics_test() returns void as $$ declare _detail text; -@@ -4096,11 +4711,24 @@ +@@ -4096,11 +4721,24 @@ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint; end; $$ language plpgsql; @@ -248268,7 +248214,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- check cases where implicit SQLSTATE variable could be confused with -- SQLSTATE as a keyword, cf bug #5524 create or replace function raise_test() returns void as $$ -@@ -4112,10 +4740,26 @@ +@@ -4112,10 +4750,26 @@ raise sqlstate '22012' using message = 'substitute message'; end; $$ language plpgsql; @@ -248298,7 +248244,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop function raise_test(); -- test passing column_name, constraint_name, datatype_name, table_name -- and schema_name error fields -@@ -4143,14 +4787,22 @@ +@@ -4143,14 +4797,22 @@ _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; end; $$ language plpgsql; @@ -248327,7 +248273,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- test variadic functions create or replace function vari(variadic int[]) returns void as $$ -@@ -4159,36 +4811,34 @@ +@@ -4159,36 +4821,34 @@ raise notice '%', $1[i]; end loop; end; $$ language plpgsql; @@ -248387,7 +248333,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- coercion test create or replace function pleast(variadic numeric[]) returns numeric as $$ -@@ -4200,30 +4850,20 @@ +@@ -4200,30 +4860,20 @@ return aux; end; $$ language plpgsql immutable strict; @@ -248428,11 +248374,11 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- in case of conflict, non-variadic version is preferred create or replace function pleast(numeric) returns numeric as $$ -@@ -4232,31 +4872,27 @@ +@@ -4232,31 +4882,27 @@ return $1; end; $$ language plpgsql immutable strict; -+ERROR: column "\"\"" does not exist ++ERROR: no value provided for placeholder: $1 select pleast(10); -NOTICE: non-variadic function called - pleast @@ -248471,7 +248417,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function tftest(a1 int) returns table(a int, b int) as $$ begin a := a1; b := a1 + 1; -@@ -4265,14 +4901,16 @@ +@@ -4265,14 +4911,16 @@ return next; end; $$ language plpgsql immutable strict; @@ -248494,7 +248440,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function rttest() returns setof int as $$ declare rc int; -@@ -4291,19 +4929,11 @@ +@@ -4291,19 +4939,11 @@ raise notice '% %', found, rc; end; $$ language plpgsql; @@ -248518,7 +248464,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- check some error cases, too create or replace function rttest() returns setof int as $$ -@@ -4311,25 +4941,26 @@ +@@ -4311,25 +4951,26 @@ return query select 10 into no_such_table; end; $$ language plpgsql; @@ -248553,7 +248499,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Test for proper cleanup at subtransaction exit. This example -- exposed a bug in PG 8.2. CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$ -@@ -4344,6 +4975,8 @@ +@@ -4344,6 +4985,8 @@ RETURN 1; END; $$ LANGUAGE plpgsql; @@ -248562,7 +248508,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER) RETURNS RECORD AS $$ BEGIN -@@ -4355,20 +4988,24 @@ +@@ -4355,20 +4998,24 @@ RETURN; END; $$ LANGUAGE plpgsql; @@ -248597,7 +248543,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Test for appropriate cleanup of non-simple expression evaluations -- (bug in all versions prior to August 2010) CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$ -@@ -4385,13 +5022,27 @@ +@@ -4385,13 +5032,27 @@ RETURN arr; END; $$ LANGUAGE plpgsql; @@ -248630,7 +248576,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$ declare i integer NOT NULL := 0; -@@ -4405,13 +5056,13 @@ +@@ -4405,13 +5066,13 @@ return i; end; $$ LANGUAGE plpgsql; @@ -248649,7 +248595,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test cases involving recursion and error recovery in simple expressions -- (bugs in all versions before October 2010). The problems are most -@@ -4427,43 +5078,56 @@ +@@ -4427,43 +5088,56 @@ end if; end; $$ language plpgsql; @@ -248719,7 +248665,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function cast_invoker(integer) returns date as $$ begin return $1; -@@ -4471,62 +5135,81 @@ +@@ -4471,62 +5145,81 @@ select cast_invoker(20150717); cast_invoker -------------- @@ -248812,7 +248758,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab end; -- Test for consistent reporting of error context create function fail() returns int language plpgsql as $$ -@@ -4534,45 +5217,29 @@ +@@ -4534,45 +5227,29 @@ return 1/0; end $$; @@ -248869,7 +248815,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab (1 row) create or replace function strtest() returns text as $$ -@@ -4625,21 +5292,16 @@ +@@ -4625,21 +5302,16 @@ RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END$$; @@ -248899,7 +248845,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab DO $$ DECLARE r record; BEGIN -@@ -4648,11 +5310,10 @@ +@@ -4648,11 +5320,10 @@ RAISE NOTICE '%, %', r.roomno, r.comment; END LOOP; END$$; @@ -248915,7 +248861,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Check handling of errors thrown from/into anonymous code blocks. do $outer$ begin -@@ -4672,16 +5333,10 @@ +@@ -4672,16 +5343,10 @@ end loop; end; $outer$; @@ -248936,7 +248882,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Check variable scoping -- a var is not available in its own or prior -- default expressions, but it is available in later ones. do $$ -@@ -4690,11 +5345,10 @@ +@@ -4690,11 +5355,10 @@ raise notice 'x = %', x; end; $$; @@ -248952,7 +248898,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare y int := x + 1; -- error x int := 42; -@@ -4702,11 +5356,10 @@ +@@ -4702,11 +5366,10 @@ raise notice 'x = %, y = %', x, y; end; $$; @@ -248968,7 +248914,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare x int := 42; y int := x + 1; -@@ -4714,7 +5367,10 @@ +@@ -4714,7 +5377,10 @@ raise notice 'x = %, y = %', x, y; end; $$; @@ -248980,7 +248926,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare x int := 42; begin -@@ -4726,7 +5382,10 @@ +@@ -4726,7 +5392,10 @@ end; end; $$; @@ -248992,7 +248938,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Check handling of conflicts between plpgsql vars and table columns. set plpgsql.variable_conflict = error; create function conflict_test() returns setof int8_tbl as $$ -@@ -4738,13 +5397,11 @@ +@@ -4738,13 +5407,11 @@ end loop; end; $$ language plpgsql; @@ -249010,7 +248956,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function conflict_test() returns setof int8_tbl as $$ #variable_conflict use_variable declare r record; -@@ -4755,16 +5412,11 @@ +@@ -4755,16 +5422,11 @@ end loop; end; $$ language plpgsql; @@ -249031,7 +248977,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function conflict_test() returns setof int8_tbl as $$ #variable_conflict use_column declare r record; -@@ -4775,17 +5427,13 @@ +@@ -4775,17 +5437,13 @@ end loop; end; $$ language plpgsql; @@ -249054,7 +249000,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Check that an unreserved keyword can be used as a variable name create function unreserved_test() returns int as $$ declare -@@ -4795,12 +5443,15 @@ +@@ -4795,12 +5453,15 @@ return forward; end $$ language plpgsql; @@ -249075,7 +249021,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function unreserved_test() returns int as $$ declare return int := 42; -@@ -4809,12 +5460,20 @@ +@@ -4809,12 +5470,20 @@ return return; end $$ language plpgsql; @@ -249101,7 +249047,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function unreserved_test() returns int as $$ declare comment int := 21; -@@ -4824,19 +5483,26 @@ +@@ -4824,19 +5493,26 @@ return comment; end $$ language plpgsql; @@ -249138,7 +249084,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test FOREACH over arrays -- -@@ -4850,26 +5516,27 @@ +@@ -4850,26 +5526,27 @@ end loop; end; $$ language plpgsql; @@ -249174,9 +249120,9 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab + +If you would rather not post publicly, please contact us directly +using the support form. -+ -+We appreciate your feedback. ++We appreciate your feedback. ++ +select foreach_test(ARRAY[1,2,3,4]); +ERROR: unknown function: foreach_test() +select foreach_test(ARRAY[[1,2],[3,4]]); @@ -249184,7 +249130,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function foreach_test(anyarray) returns void as $$ declare x int; -@@ -4880,13 +5547,28 @@ +@@ -4880,13 +5557,28 @@ end loop; end; $$ language plpgsql; @@ -249217,7 +249163,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function foreach_test(anyarray) returns void as $$ declare x int[]; -@@ -4897,21 +5579,27 @@ +@@ -4897,21 +5589,27 @@ end loop; end; $$ language plpgsql; @@ -249245,10 +249191,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. - ++ +If you would rather not post publicly, please contact us directly +using the support form. -+ + +We appreciate your feedback. + +select foreach_test(ARRAY[1,2,3,4]); @@ -249258,7 +249204,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- higher level of slicing create or replace function foreach_test(anyarray) returns void as $$ -@@ -4923,26 +5611,31 @@ +@@ -4923,26 +5621,31 @@ end loop; end; $$ language plpgsql; @@ -249305,7 +249251,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create type xy_tuple AS (x int, y int); -- iteration over array of records create or replace function foreach_test(anyarray) -@@ -4955,25 +5648,27 @@ +@@ -4955,25 +5658,27 @@ end loop; end; $$ language plpgsql; @@ -249337,10 +249283,10 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab +Please check the public issue tracker to check whether this problem is +already tracked. If you cannot find it there, please report the error +with details by creating a new issue. - ++ +If you would rather not post publicly, please contact us directly +using the support form. -+ + +We appreciate your feedback. + +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); @@ -249350,7 +249296,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function foreach_test(anyarray) returns void as $$ declare x int; y int; -@@ -4984,25 +5679,27 @@ +@@ -4984,25 +5689,27 @@ end loop; end; $$ language plpgsql; @@ -249395,7 +249341,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- slicing over array of composite types create or replace function foreach_test(anyarray) returns void as $$ -@@ -5014,22 +5711,29 @@ +@@ -5014,22 +5721,29 @@ end loop; end; $$ language plpgsql; @@ -249438,7 +249384,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab drop type xy_tuple; -- -- Assorted tests for array subscript assignment -@@ -5043,28 +5747,34 @@ +@@ -5043,28 +5757,34 @@ r.ar[2] := 'replace'; return r.ar; end$$; @@ -249489,7 +249435,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create function testoa(x1 int, x2 int, x3 int) returns orderedarray language plpgsql as $$ declare res orderedarray; -@@ -5073,26 +5783,19 @@ +@@ -5073,26 +5793,19 @@ res[2] := x3; return res; end$$; @@ -249523,7 +249469,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test handling of expanded arrays -- -@@ -5116,17 +5819,11 @@ +@@ -5116,17 +5829,11 @@ select i, a from (select returns_rw_array(1) as a offset 0) ss, lateral consumes_rw_array(a) i; @@ -249546,7 +249492,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab select i, a from (select returns_rw_array(1) as a offset 0) ss, lateral consumes_rw_array(a) i; -@@ -5137,13 +5834,11 @@ +@@ -5137,13 +5844,11 @@ explain (verbose, costs off) select consumes_rw_array(a), a from returns_rw_array(1) a; @@ -249565,7 +249511,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab select consumes_rw_array(a), a from returns_rw_array(1) a; consumes_rw_array | a -------------------+------- -@@ -5153,12 +5848,11 @@ +@@ -5153,12 +5858,11 @@ explain (verbose, costs off) select consumes_rw_array(a), a from (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); @@ -249583,7 +249529,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab select consumes_rw_array(a), a from (values (returns_rw_array(1)), (returns_rw_array(2))) v(a); consumes_rw_array | a -@@ -5173,7 +5867,10 @@ +@@ -5173,7 +5877,10 @@ a := a || 3; raise notice 'a = %', a; end$$; @@ -249595,7 +249541,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test access to call stack -- -@@ -5190,6 +5887,18 @@ +@@ -5190,6 +5897,18 @@ return 2 * $1; end; $$ language plpgsql; @@ -249614,7 +249560,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function outer_func(int) returns int as $$ declare -@@ -5201,6 +5910,8 @@ +@@ -5201,6 +5920,8 @@ return myresult; end; $$ language plpgsql; @@ -249623,7 +249569,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function outer_outer_func(int) returns int as $$ declare -@@ -5212,44 +5923,19 @@ +@@ -5212,44 +5933,19 @@ return myresult; end; $$ language plpgsql; @@ -249675,7 +249621,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- access to call stack from exception create function inner_func(int) returns int as $$ -@@ -5272,6 +5958,26 @@ +@@ -5272,6 +5968,26 @@ return 2 * $1; end; $$ language plpgsql; @@ -249702,7 +249648,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function outer_func(int) returns int as $$ declare -@@ -5283,6 +5989,8 @@ +@@ -5283,6 +5999,8 @@ return myresult; end; $$ language plpgsql; @@ -249711,7 +249657,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab create or replace function outer_outer_func(int) returns int as $$ declare -@@ -5294,44 +6002,19 @@ +@@ -5294,44 +6012,19 @@ return myresult; end; $$ language plpgsql; @@ -249763,7 +249709,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- Test pg_routine_oid create function current_function(text) returns regprocedure as $$ -@@ -5342,13 +6025,17 @@ +@@ -5342,13 +6035,17 @@ return fn_oid; end; $$ language plpgsql; @@ -249786,7 +249732,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- shouldn't fail in DO, even though there's no useful data do $$ declare -@@ -5358,7 +6045,10 @@ +@@ -5358,7 +6055,10 @@ raise notice 'pg_routine_oid = %', fn_oid; end; $$; @@ -249798,7 +249744,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test ASSERT -- -@@ -5367,20 +6057,28 @@ +@@ -5367,20 +6067,28 @@ assert 1=1; -- should succeed end; $$; @@ -249831,7 +249777,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- check controlling GUC set plpgsql.check_asserts = off; do $$ -@@ -5388,6 +6086,10 @@ +@@ -5388,6 +6096,10 @@ assert 1=0; -- won't be tested end; $$; @@ -249842,7 +249788,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab reset plpgsql.check_asserts; -- test custom message do $$ -@@ -5396,8 +6098,10 @@ +@@ -5396,8 +6108,10 @@ assert 1=0, format('assertion failed, var = "%s"', var); end; $$; @@ -249855,7 +249801,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- ensure assertions are not trapped by 'others' do $$ begin -@@ -5406,32 +6110,52 @@ +@@ -5406,32 +6120,52 @@ null; -- do nothing end; $$; @@ -249912,7 +249858,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab do $$ declare v_test plpgsql_arr_domain; begin -@@ -5439,14 +6163,20 @@ +@@ -5439,14 +6173,20 @@ v_test := v_test || 2; end; $$; @@ -249935,7 +249881,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- test usage of transition tables in AFTER triggers -- -@@ -5472,25 +6202,31 @@ +@@ -5472,25 +6212,31 @@ RETURN new; END; $$; @@ -249974,7 +249920,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE OR REPLACE FUNCTION transition_table_base_upd_func() RETURNS trigger LANGUAGE plpgsql -@@ -5512,30 +6248,28 @@ +@@ -5512,30 +6258,28 @@ RETURN new; END; $$; @@ -250013,7 +249959,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE TABLE transition_table_level2 ( level2_no serial NOT NULL , -@@ -5543,6 +6277,7 @@ +@@ -5543,6 +6287,7 @@ level1_node_name varchar(255), PRIMARY KEY (level2_no) ) WITHOUT OIDS; @@ -250021,7 +249967,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE TABLE transition_table_status ( level int NOT NULL, -@@ -5563,11 +6298,18 @@ +@@ -5563,11 +6308,18 @@ RETURN NULL; END; $$; @@ -250040,7 +249986,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE FUNCTION transition_table_level1_ri_parent_upd_func() RETURNS TRIGGER LANGUAGE plpgsql -@@ -5590,11 +6332,18 @@ +@@ -5590,11 +6342,18 @@ RETURN NULL; END; $$; @@ -250059,7 +250005,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE FUNCTION transition_table_level2_ri_child_insupd_func() RETURNS TRIGGER LANGUAGE plpgsql -@@ -5610,16 +6359,29 @@ +@@ -5610,16 +6369,29 @@ RETURN NULL; END; $$; @@ -250089,7 +250035,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- create initial test data INSERT INTO transition_table_level1 (level1_no) SELECT generate_series(1,200); -@@ -5627,6 +6389,7 @@ +@@ -5627,6 +6399,7 @@ INSERT INTO transition_table_level2 (level2_no, parent_no) SELECT level2_no, level2_no / 50 + 1 AS parent_no FROM generate_series(1,9999) level2_no; @@ -250097,7 +250043,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab ANALYZE transition_table_level2; INSERT INTO transition_table_status (level, node_no, status) SELECT 1, level1_no, 0 FROM transition_table_level1; -@@ -5646,35 +6409,37 @@ +@@ -5646,35 +6419,37 @@ RETURN NULL; END; $$; @@ -250146,7 +250092,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- attempt modifications which would not break RI (should all succeed) DELETE FROM transition_table_level1 WHERE level1_no BETWEEN 201 AND 1000; -@@ -5683,7 +6448,7 @@ +@@ -5683,7 +6458,7 @@ SELECT count(*) FROM transition_table_level1; count ------- @@ -250155,7 +250101,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab (1 row) DELETE FROM transition_table_level2 -@@ -5691,7 +6456,7 @@ +@@ -5691,7 +6466,7 @@ SELECT count(*) FROM transition_table_level2; count ------- @@ -250164,7 +250110,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab (1 row) CREATE TABLE alter_table_under_transition_tables -@@ -5711,42 +6476,49 @@ +@@ -5711,42 +6486,49 @@ RETURN NULL; END; $$; @@ -250224,7 +250170,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Test multiple reference to a transition table -- -@@ -5761,22 +6533,45 @@ +@@ -5761,22 +6543,45 @@ FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss); RETURN NULL; END$$; @@ -250272,7 +250218,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE OR REPLACE FUNCTION get_from_partitioned_table(partitioned_table.a%type) RETURNS partitioned_table AS $$ DECLARE -@@ -5787,13 +6582,13 @@ +@@ -5787,13 +6592,13 @@ SELECT * INTO result FROM partitioned_table WHERE a = a_val; RETURN result; END; $$ LANGUAGE plpgsql; @@ -250292,7 +250238,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab CREATE OR REPLACE FUNCTION list_partitioned_table() RETURNS SETOF partitioned_table.a%TYPE AS $$ DECLARE -@@ -5806,14 +6601,13 @@ +@@ -5806,14 +6611,13 @@ END LOOP; RETURN; END; $$ LANGUAGE plpgsql; @@ -250313,7 +250259,7 @@ diff -U3 --label=/mnt/data1/postgres/src/test/regress/expected/plpgsql.out --lab -- -- Check argument name is used instead of $n in error message -- -@@ -5822,6 +6616,9 @@ +@@ -5822,6 +6626,9 @@ GET DIAGNOSTICS x = ROW_COUNT; RETURN; END; $$ LANGUAGE plpgsql; diff --git a/pkg/cmd/roachtest/tests/pgjdbc_blocklist.go b/pkg/cmd/roachtest/tests/pgjdbc_blocklist.go index b1b2e4135e95..f5788279270e 100644 --- a/pkg/cmd/roachtest/tests/pgjdbc_blocklist.go +++ b/pkg/cmd/roachtest/tests/pgjdbc_blocklist.go @@ -487,10 +487,8 @@ var pgjdbcBlockList = blocklist{ `org.postgresql.test.jdbc3.CompositeTest.testSimpleSelect`: "27793", `org.postgresql.test.jdbc3.CompositeTest.testTableMetadata`: "27793", `org.postgresql.test.jdbc3.DatabaseMetaDataTest.testGetColumnsForDomain`: "27796", - `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallIfNoReturnTest.testInvokeFunction`: "100405", `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallIfNoReturnTest.testInvokeFunctionHavingReturnParameter`: "17511", `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallIfNoReturnTest.testInvokeProcedure`: "17511", - `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallTest.testInvokeFunction`: "100405", `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallTest.testInvokeFunctionHavingReturnParameter`: "17511", `org.postgresql.test.jdbc3.EscapeSyntaxCallModeCallTest.testInvokeProcedure`: "17511", `org.postgresql.test.jdbc3.EscapeSyntaxCallModeSelectTest.testInvokeFunction`: "17511", diff --git a/pkg/sql/alter_function.go b/pkg/sql/alter_function.go index 3713088bc854..f63e275b7fd4 100644 --- a/pkg/sql/alter_function.go +++ b/pkg/sql/alter_function.go @@ -462,7 +462,7 @@ func toSchemaOverloadSignature(fnDesc *funcdesc.Mutable) descpb.SchemaDescriptor IsProcedure: fnDesc.IsProcedure(), } for _, param := range fnDesc.Params { - if tree.IsInParamClass(funcdesc.ToTreeRoutineParamClass(param.Class)) { + if tree.IsParamIncludedIntoSignature(funcdesc.ToTreeRoutineParamClass(param.Class), ret.IsProcedure) { ret.ArgTypes = append(ret.ArgTypes, param.Type) } } diff --git a/pkg/sql/catalog/descpb/structured.proto b/pkg/sql/catalog/descpb/structured.proto index 389284c33ad2..0362504bc24f 100644 --- a/pkg/sql/catalog/descpb/structured.proto +++ b/pkg/sql/catalog/descpb/structured.proto @@ -1551,7 +1551,8 @@ message SchemaDescriptor { optional uint32 id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ID", (gogoproto.casttype) = "ID"]; - // ArgTypes contains only IN / INOUT parameters. + // ArgTypes contains only IN / INOUT parameters when IsProcedure is false, + // and all parameters when IsProcedure is true. repeated sql.sem.types.T arg_types = 2; optional sql.sem.types.T return_type = 3; @@ -1621,7 +1622,8 @@ message FunctionDescriptor { optional uint32 parent_schema_id = 4 [(gogoproto.nullable) = false, (gogoproto.customname) = "ParentSchemaID", (gogoproto.casttype) = "ID"]; - // params represents the list of parameters of the UDF signature. + // params represents the list of parameters, regardless of the parameter + // class, of the UDF signature. repeated Parameter params = 5 [(gogoproto.nullable) = false]; // return_type represents the return type of the UDF signature. diff --git a/pkg/sql/catalog/funcdesc/func_desc.go b/pkg/sql/catalog/funcdesc/func_desc.go index 6641df199240..73df0eabd48c 100644 --- a/pkg/sql/catalog/funcdesc/func_desc.go +++ b/pkg/sql/catalog/funcdesc/func_desc.go @@ -708,33 +708,34 @@ func (desc *immutable) ToOverload() (ret *tree.Overload, err error) { routineType = tree.ProcedureRoutine } ret = &tree.Overload{ - Oid: catid.FuncIDToOID(desc.ID), - Body: desc.FunctionBody, - Type: routineType, - Version: uint64(desc.Version), - Language: desc.getCreateExprLang(), + Oid: catid.FuncIDToOID(desc.ID), + Body: desc.FunctionBody, + Type: routineType, + Version: uint64(desc.Version), + Language: desc.getCreateExprLang(), + RoutineParams: make(tree.RoutineParams, 0, len(desc.Params)), } signatureTypes := make(tree.ParamTypes, 0, len(desc.Params)) - var firstOutParamName string var outParamNames []string for _, param := range desc.Params { class := ToTreeRoutineParamClass(param.Class) - if tree.IsInParamClass(class) { - // Only IN parameters should be included into the signature of this - // function overload. + if tree.IsParamIncludedIntoSignature(class, desc.IsProcedure()) { signatureTypes = append(signatureTypes, tree.ParamType{Name: param.Name, Typ: param.Type}) } if tree.IsOutParamClass(class) { paramName := param.Name - if len(outParamNames) == 0 { - firstOutParamName = paramName - } if paramName == "" { paramName = fmt.Sprintf("column%d", len(outParamNames)+1) } outParamNames = append(outParamNames, paramName) } + ret.RoutineParams = append(ret.RoutineParams, tree.RoutineParam{ + Name: tree.Name(param.Name), + Type: param.Type, + Class: class, + // TODO(100962): populate DefaultVal. + }) } returnType := desc.ReturnType.Type if types.IsRecordType(returnType) { @@ -743,10 +744,6 @@ func (desc *immutable) ToOverload() (ret *tree.Overload, err error) { } ret.ReturnType = tree.FixedReturnType(returnType) ret.Types = signatureTypes - if len(outParamNames) == 1 { - ret.NamedReturnColumn = firstOutParamName - } - ret.HasNamedReturnColumns = len(outParamNames) > 1 ret.Volatility, err = desc.getOverloadVolatility() if err != nil { return nil, err diff --git a/pkg/sql/catalog/funcdesc/func_desc_test.go b/pkg/sql/catalog/funcdesc/func_desc_test.go index d26e29966d58..276ca64ace9e 100644 --- a/pkg/sql/catalog/funcdesc/func_desc_test.go +++ b/pkg/sql/catalog/funcdesc/func_desc_test.go @@ -673,6 +673,9 @@ func TestToOverload(t *testing.T) { Body: "ANY QUERIES", Type: tree.UDFRoutine, Language: tree.RoutineLangSQL, + RoutineParams: tree.RoutineParams{ + {Name: "arg1", Type: types.Int}, + }, }, }, { @@ -697,6 +700,9 @@ func TestToOverload(t *testing.T) { Body: "ANY QUERIES", Type: tree.UDFRoutine, Language: tree.RoutineLangSQL, + RoutineParams: tree.RoutineParams{ + {Name: "arg1", Type: types.Int}, + }, }, }, { @@ -722,6 +728,9 @@ func TestToOverload(t *testing.T) { Body: "ANY QUERIES", Type: tree.UDFRoutine, Language: tree.RoutineLangSQL, + RoutineParams: tree.RoutineParams{ + {Name: "arg1", Type: types.Int}, + }, }, }, { @@ -748,6 +757,9 @@ func TestToOverload(t *testing.T) { Type: tree.UDFRoutine, CalledOnNullInput: true, Language: tree.RoutineLangSQL, + RoutineParams: tree.RoutineParams{ + {Name: "arg1", Type: types.Int}, + }, }, }, { @@ -772,6 +784,9 @@ func TestToOverload(t *testing.T) { Body: "ANY QUERIES", Type: tree.UDFRoutine, Language: tree.RoutineLangSQL, + RoutineParams: tree.RoutineParams{ + {Name: "arg1", Type: types.Int}, + }, }, err: "function 1 is leakproof but not immutable", }, diff --git a/pkg/sql/catalog/schemadesc/schema_desc.go b/pkg/sql/catalog/schemadesc/schema_desc.go index ae4bcf7a6740..fc529698c521 100644 --- a/pkg/sql/catalog/schemadesc/schema_desc.go +++ b/pkg/sql/catalog/schemadesc/schema_desc.go @@ -526,6 +526,9 @@ func (desc *immutable) GetResolvedFuncDefinition( if funcDescPb.Signatures[i].ReturnSet { overload.Class = tree.GeneratorClass } + // There is no need to look at the parameter classes since ArgTypes + // already contains only parameters that are included into the + // signature of the overload. paramTypes := make(tree.ParamTypes, 0, len(sig.ArgTypes)) for _, paramType := range sig.ArgTypes { paramTypes = append( diff --git a/pkg/sql/crdb_internal.go b/pkg/sql/crdb_internal.go index 98f4a9191f97..31db03c41dfe 100644 --- a/pkg/sql/crdb_internal.go +++ b/pkg/sql/crdb_internal.go @@ -2674,10 +2674,13 @@ func populateQueriesTable( addRow func(...tree.Datum) error, response *serverpb.ListSessionsResponse, ) error { - shouldRedactQuery := false + shouldRedactOtherUserQuery := false + canViewOtherUser := false // Check if the user is admin. if isAdmin, err := p.HasAdminRole(ctx); err != nil { return err + } else if isAdmin { + canViewOtherUser = true } else if !isAdmin { // If the user is not admin, check the individual VIEWACTIVITY and VIEWACTIVITYREDACTED // privileges. @@ -2686,16 +2689,24 @@ func populateQueriesTable( } else if hasViewActivityRedacted { // If the user has VIEWACTIVITYREDACTED, redact the query as it takes precedence // over VIEWACTIVITY. - shouldRedactQuery = true - } else if hasViewActivity, err := p.HasViewActivity(ctx); err != nil { - return err - } else if !hasViewActivity { - // If the user is not admin and does not have VIEWACTIVITY or VIEWACTIVITYREDACTED, - // return insufficient privileges error. - return noViewActivityOrViewActivityRedactedRoleError(p.User()) + shouldRedactOtherUserQuery = true + canViewOtherUser = true + } else if !hasViewActivityRedacted { + if hasViewActivity, err := p.HasViewActivity(ctx); err != nil { + return err + } else if hasViewActivity { + canViewOtherUser = true + } } } for _, session := range response.Sessions { + normalizedUser, err := username.MakeSQLUsernameFromUserInput(session.Username, username.PurposeValidation) + if err != nil { + return err + } + if !canViewOtherUser && normalizedUser != p.User() { + continue + } sessionID := getSessionID(session) for _, query := range session.ActiveQueries { isDistributedDatum := tree.DNull @@ -2739,8 +2750,9 @@ func populateQueriesTable( // Interpolate placeholders into the SQL statement. sql := formatActiveQuery(query) - // If the user does not have the correct privileges, show the query without literals or constants. - if shouldRedactQuery { + // If the user does not have the correct privileges, show the query + // without literals or constants. + if shouldRedactOtherUserQuery && session.Username != p.SessionData().User().Normalized() { sql = query.SqlNoConstants } if err := addRow( @@ -2856,7 +2868,7 @@ var crdbInternalLocalSessionsTable = virtualSchemaTable{ if err != nil { return err } - return populateSessionsTable(ctx, addRow, response) + return populateSessionsTable(ctx, p, addRow, response) }, } @@ -2874,14 +2886,49 @@ var crdbInternalClusterSessionsTable = virtualSchemaTable{ if err != nil { return err } - return populateSessionsTable(ctx, addRow, response) + return populateSessionsTable(ctx, p, addRow, response) }, } func populateSessionsTable( - ctx context.Context, addRow func(...tree.Datum) error, response *serverpb.ListSessionsResponse, + ctx context.Context, + p *planner, + addRow func(...tree.Datum) error, + response *serverpb.ListSessionsResponse, ) error { + shouldRedactOtherUserQuery := false + canViewOtherUser := false + // Check if the user is admin. + if isAdmin, err := p.HasAdminRole(ctx); err != nil { + return err + } else if isAdmin { + canViewOtherUser = true + } else if !isAdmin { + // If the user is not admin, check the VIEWACTIVITYREDACTED privilege to + // see if constants need to be redacted. + if hasViewActivityRedacted, err := p.HasViewActivityRedacted(ctx); err != nil { + return err + } else if hasViewActivityRedacted { + // If the user has VIEWACTIVITYREDACTED, redact the query as it takes precedence + // over VIEWACTIVITY. + shouldRedactOtherUserQuery = true + canViewOtherUser = true + } else if !hasViewActivityRedacted { + if hasViewActivity, err := p.HasViewActivity(ctx); err != nil { + return err + } else if hasViewActivity { + canViewOtherUser = true + } + } + } for _, session := range response.Sessions { + normalizedUser, err := username.MakeSQLUsernameFromUserInput(session.Username, username.PurposeValidation) + if err != nil { + return err + } + if !canViewOtherUser && normalizedUser != p.User() { + continue + } // Generate active_queries and active_query_start var activeQueries bytes.Buffer var activeQueryStart time.Time @@ -2893,10 +2940,18 @@ func populateSessionsTable( // queries to be executed at once in a session. activeQueryStart = query.Start sql := formatActiveQuery(query) + // Never redact queries made by the same user. + if shouldRedactOtherUserQuery && session.Username != p.User().Normalized() { + sql = query.SqlNoConstants + } activeQueries.WriteString(sql) } + lastActiveQuery := session.LastActiveQuery + // Never redact queries made by the same user. + if shouldRedactOtherUserQuery && session.Username != p.User().Normalized() { + lastActiveQuery = session.LastActiveQueryNoConstants + } - var err error if activeQueryStart.IsZero() { activeQueryStartDatum = tree.DNull } else { @@ -2930,7 +2985,7 @@ func populateSessionsTable( tree.NewDString(session.ClientAddress), tree.NewDString(session.ApplicationName), tree.NewDString(activeQueries.String()), - tree.NewDString(session.LastActiveQuery), + tree.NewDString(lastActiveQuery), tree.NewDInt(tree.DInt(session.NumTxnsExecuted)), startTSDatum, activeQueryStartDatum, diff --git a/pkg/sql/create_function.go b/pkg/sql/create_function.go index 8583591373bc..f68713310f85 100644 --- a/pkg/sql/create_function.go +++ b/pkg/sql/create_function.go @@ -141,7 +141,7 @@ func (n *createFunctionNode) createNewFunction( } signatureTypes := make([]*types.T, 0, len(udfDesc.Params)) for _, param := range udfDesc.Params { - if tree.IsInParamClass(funcdesc.ToTreeRoutineParamClass(param.Class)) { + if tree.IsParamIncludedIntoSignature(funcdesc.ToTreeRoutineParamClass(param.Class), udfDesc.IsProcedure()) { signatureTypes = append(signatureTypes, param.Type) } } diff --git a/pkg/sql/logictest/testdata/logic_test/udf_params b/pkg/sql/logictest/testdata/logic_test/udf_params index 59ffa768f996..5dd6ed8279c4 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf_params +++ b/pkg/sql/logictest/testdata/logic_test/udf_params @@ -172,10 +172,6 @@ CREATE FUNCTION f_same_name(OUT a INT, INOUT a INT) IMMUTABLE LANGUAGE SQL AS $$ statement ok CREATE FUNCTION f_same_name(IN a INT, OUT a INT) IMMUTABLE LANGUAGE SQL AS $$ SELECT 1 $$; -# TODO(100405): this should work. -# statement ok -# CREATE FUNCTION f_same_name(IN a INT, OUT a INT) IMMUTABLE LANGUAGE SQL AS $$ SELECT a $$; - query I colnames SELECT f_same_name(2); ---- @@ -188,6 +184,39 @@ SELECT * FROM f_same_name(2); a 1 +statement ok +CREATE OR REPLACE FUNCTION f_same_name(IN a INT, OUT a INT) IMMUTABLE LANGUAGE SQL AS $$ SELECT a $$; + +query I colnames +SELECT f_same_name(2); +---- +f_same_name +2 + +query I colnames +SELECT * FROM f_same_name(2); +---- +a +2 + +statement error pgcode 42703 pq: column "param_out" does not exist +CREATE FUNCTION f_names(IN param_in INT, OUT param_out INT) IMMUTABLE LANGUAGE SQL AS $$ SELECT param_out $$; + +statement ok +CREATE FUNCTION f_names(IN param_in INT, OUT param_out INT) IMMUTABLE LANGUAGE SQL AS $$ SELECT param_in $$; + +query I colnames +SELECT f_names(2); +---- +f_names +2 + +query I colnames +SELECT * FROM f_names(2); +---- +param_out +2 + statement ok CREATE FUNCTION f_out_int(OUT param INT) RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; @@ -381,24 +410,16 @@ param # We can change the order of parameters across IN and OUT "namespaces" as long # as we preserve the right ordering within each "namespace". statement ok -CREATE FUNCTION f_3_in_2_out(IN param1 INT, IN param2 INT, IN param3 INT, OUT param1 INT, OUT param2 INT) AS $$ SELECT (1, 1); $$ LANGUAGE SQL; - -# TODO(100405): this should work. -# statement ok -# CREATE FUNCTION f_3_in_2_out(IN param1 INT, IN param2 INT, IN param3 INT, OUT param1 INT, OUT param2 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; +CREATE FUNCTION f_3_in_2_out(IN param1 INT, IN param2 INT, IN param3 INT, OUT param1 INT, OUT param2 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; query II colnames SELECT * FROM f_3_in_2_out(2, 2, 2); ---- param1 param2 -1 1 +2 4 statement ok -CREATE OR REPLACE FUNCTION f_3_in_2_out(IN param1 INT, OUT param1 INT, IN param2 INT, IN param3 INT, OUT param2 INT) AS $$ SELECT (1, 1); $$ LANGUAGE SQL; - -# TODO(100405): this should work. -# statement ok -# CREATE OR REPLACE FUNCTION f_3_in_2_out(IN param1 INT, OUT param1 INT, IN param2 INT, IN param3 INT, OUT param2 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION f_3_in_2_out(IN param1 INT, OUT param1 INT, IN param2 INT, IN param3 INT, OUT param2 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; query T SELECT create_statement FROM [SHOW CREATE FUNCTION f_3_in_2_out]; @@ -410,25 +431,21 @@ CREATE FUNCTION public.f_3_in_2_out(IN param1 INT8, OUT param1 INT8, IN param2 I CALLED ON NULL INPUT LANGUAGE SQL AS $$ - SELECT (1:::INT8, 1:::INT8); + SELECT (param1, param2 + param3); $$ query II colnames SELECT * FROM f_3_in_2_out(2, 2, 2); ---- param1 param2 -1 1 +2 4 # We can also merge some parameters as long as they have the same names. statement error pgcode 42P13 pq: cannot change return type of existing function CREATE OR REPLACE FUNCTION f_3_in_2_out(INOUT param1 INT, IN param2 INT, INOUT param3 INT) AS $$ SELECT (1, 1); $$ LANGUAGE SQL; statement ok -CREATE OR REPLACE FUNCTION f_3_in_2_out(INOUT param1 INT, INOUT param2 INT, IN param3 INT) AS $$ SELECT (1, 1); $$ LANGUAGE SQL; - -# TODO(100405): this should work. -# statement ok -# CREATE OR REPLACE FUNCTION f_3_in_2_out(INOUT param1 INT, INOUT param2 INT, IN param3 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION f_3_in_2_out(INOUT param1 INT, INOUT param2 INT, IN param3 INT) AS $$ SELECT (param1, param2 + param3); $$ LANGUAGE SQL; query T SELECT create_statement FROM [SHOW CREATE FUNCTION f_3_in_2_out]; @@ -440,14 +457,14 @@ CREATE FUNCTION public.f_3_in_2_out(INOUT param1 INT8, INOUT param2 INT8, IN par CALLED ON NULL INPUT LANGUAGE SQL AS $$ - SELECT (1:::INT8, 1:::INT8); + SELECT (param1, param2 + param3); $$ query II colnames SELECT * FROM f_3_in_2_out(2, 2, 2); ---- param1 param2 -1 1 +2 4 subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/udf_rewrite b/pkg/sql/logictest/testdata/logic_test/udf_rewrite index b87a80ad224a..e78cca5437ae 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf_rewrite +++ b/pkg/sql/logictest/testdata/logic_test/udf_rewrite @@ -93,6 +93,34 @@ SELECT get_body_str('f_rewrite'); statement ok DROP FUNCTION f_rewrite(); +statement ok +CREATE FUNCTION f_rewrite(OUT weekday) AS +$$ + SELECT 'thursday'::weekday; +$$ LANGUAGE SQL + +query T +SELECT get_body_str('f_rewrite'); +---- +"SELECT b'\\xa0':::@100107;" + +statement ok +DROP FUNCTION f_rewrite(); + +statement ok +CREATE FUNCTION f_rewrite(INOUT weekday) AS +$$ + SELECT 'thursday'::weekday; +$$ LANGUAGE SQL + +query T +SELECT get_body_str('f_rewrite'); +---- +"SELECT b'\\xa0':::@100107;" + +statement ok +DROP FUNCTION f_rewrite; + subtest end subtest rewrite_proc diff --git a/pkg/sql/opt/optbuilder/create_function.go b/pkg/sql/opt/optbuilder/create_function.go index 23521417908d..45a47f7b4a59 100644 --- a/pkg/sql/opt/optbuilder/create_function.go +++ b/pkg/sql/opt/optbuilder/create_function.go @@ -151,14 +151,26 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o b.semaCtx.Annotations = oldSemaCtxAnn } - paramNameSeenIn, paramNameSeenOut := make(map[tree.Name]struct{}), make(map[tree.Name]struct{}) - for _, param := range cf.Params { - if param.Name != "" { - if param.IsInParam() { - checkDuplicateParamName(param, paramNameSeenIn) + // TODO(100405): check this logic for procedures. + if language == tree.RoutineLangPLpgSQL { + paramNameSeen := make(map[tree.Name]struct{}) + for _, param := range cf.Params { + if param.Name != "" { + checkDuplicateParamName(param, paramNameSeen) } - if param.IsOutParam() { - checkDuplicateParamName(param, paramNameSeenOut) + } + } else { + // For SQL routines, input and output parameters form separate + // "namespaces". + paramNameSeenIn, paramNameSeenOut := make(map[tree.Name]struct{}), make(map[tree.Name]struct{}) + for _, param := range cf.Params { + if param.Name != "" { + if param.IsInParam() { + checkDuplicateParamName(param, paramNameSeenIn) + } + if param.IsOutParam() { + checkDuplicateParamName(param, paramNameSeenOut) + } } } } @@ -167,11 +179,11 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o // named parameters to the scope so that references to them in the body can // be resolved. bodyScope := b.allocScope() - var paramTypes tree.ParamTypes + // routineParams are all parameters of PLpgSQL routines. + var routineParams []routineParam var outParamTypes []*types.T // When multiple OUT parameters are present, parameter names become the // labels in the output RECORD type. - // TODO(#100405): this needs to be checked for PLpgSQL routines. var outParamNames []string for i := range cf.Params { param := &cf.Params[i] @@ -201,10 +213,12 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o } } - // Add the parameter to the base scope of the body. - paramColName := funcParamColName(param.Name, i) - col := b.synthesizeColumn(bodyScope, paramColName, typ, nil /* expr */, nil /* scalar */) - col.setParamOrd(i) + // Add this parameter to the base scope of the body if needed. + if tree.IsParamIncludedIntoSignature(param.Class, cf.IsProcedure) { + paramColName := funcParamColName(param.Name, i) + col := b.synthesizeColumn(bodyScope, paramColName, typ, nil /* expr */, nil /* scalar */) + col.setParamOrd(i) + } // Collect the user defined type dependencies. typedesc.GetTypeDescriptorClosure(typ).ForEach(func(id descpb.ID) { @@ -213,9 +227,10 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o // Collect the parameters for PLpgSQL routines. if language == tree.RoutineLangPLpgSQL { - paramTypes = append(paramTypes, tree.ParamType{ - Name: param.Name.String(), - Typ: typ, + routineParams = append(routineParams, routineParam{ + name: param.Name, + typ: typ, + class: param.Class, }) } } @@ -325,9 +340,9 @@ func (b *Builder) buildCreateFunction(cf *tree.CreateRoutine, inScope *scope) (o // the volatility. b.factory.FoldingControl().TemporarilyDisallowStableFolds(func() { plBuilder := newPLpgSQLBuilder( - b, cf.Name.Object(), nil /* colRefs */, paramTypes, funcReturnType, + b, cf.Name.Object(), nil /* colRefs */, routineParams, funcReturnType, ) - stmtScope = plBuilder.buildRootBlock(stmt.AST, bodyScope) + stmtScope = plBuilder.buildRootBlock(stmt.AST, bodyScope, routineParams) }) checkStmtVolatility(targetVolatility, stmtScope, stmt) diff --git a/pkg/sql/opt/optbuilder/plpgsql.go b/pkg/sql/opt/optbuilder/plpgsql.go index 06fdbd6adfa1..e4570877c8bd 100644 --- a/pkg/sql/opt/optbuilder/plpgsql.go +++ b/pkg/sql/opt/optbuilder/plpgsql.go @@ -167,13 +167,25 @@ type plpgsqlBuilder struct { blocks []plBlock identCounter int + + // hasOutParam indicates whether the routine has at least one OUT / INOUT + // parameter. + hasOutParam bool +} + +// routineParam is similar to tree.RoutineParam but stores the resolved type. +type routineParam struct { + name ast.Variable + typ *types.T + class tree.RoutineParamClass + // TODO(100962): populate DefaultVal. } func newPLpgSQLBuilder( ob *Builder, routineName string, colRefs *opt.ColSet, - params []tree.ParamType, + routineParams []routineParam, returnType *types.T, ) *plpgsqlBuilder { const initialBlocksCap = 2 @@ -187,11 +199,18 @@ func newPLpgSQLBuilder( // PL/pgSQL variables. b.pushBlock(plBlock{ label: routineName, - vars: make([]ast.Variable, 0, len(params)), + vars: make([]ast.Variable, 0, len(routineParams)), varTypes: make(map[ast.Variable]*types.T), }) - for _, param := range params { - b.addVariable(ast.Variable(param.Name), param.Typ) + for _, param := range routineParams { + if param.name != "" { + // TODO(119502): unnamed parameters can only be accessed via $i + // notation. + b.addVariable(param.name, param.typ) + } + if tree.IsOutParamClass(param.class) { + b.hasOutParam = true + } } return b } @@ -229,14 +248,25 @@ type plBlock struct { } // buildRootBlock builds a PL/pgSQL routine starting with the root block. -func (b *plpgsqlBuilder) buildRootBlock(astBlock *ast.Block, s *scope) *scope { +func (b *plpgsqlBuilder) buildRootBlock( + astBlock *ast.Block, s *scope, routineParams []routineParam, +) *scope { // Push the scope so that the routine parameters live on a parent scope // instead of the current one. This indicates that the columns are "outer" // columns, which can be referenced but do not originate from an input // expression. If we don't do this, the result would be internal errors due // to Project expressions that try to "pass through" input columns that aren't // actually produced by the input expression. - return b.buildBlock(astBlock, s.push()) + s = s.push() + b.ensureScopeHasExpr(s) + // Initialize OUT parameters to NULL. + for _, param := range routineParams { + if param.class != tree.RoutineParamOut || param.name == "" { + continue + } + s = b.addPLpgSQLAssign(s, param.name, &tree.CastExpr{Expr: tree.DNull, Type: param.typ}) + } + return b.buildBlock(astBlock, s) } // buildBlock constructs an expression that returns the result of executing a @@ -357,6 +387,11 @@ func (b *plpgsqlBuilder) buildPLpgSQLStatements(stmts []ast.Statement, s *scope) return b.buildBlock(t, s) case *ast.Return: + if b.hasOutParam && !t.Implicit { + // TODO(yuzefovich): we should not error out here if we have an + // empty RETURN (which is currently not supported by parser). + panic(returnWithOUTParameterErr) + } // RETURN is handled by projecting a single column with the expression // that is being returned. returnScalar := b.buildPLpgSQLExpr(t.Expr, b.returnType, s) @@ -1758,4 +1793,7 @@ var ( nestedBlockExceptionErr = unimplemented.New("exception handler for nested blocks", "PL/pgSQL blocks cannot yet be nested within a block that has an exception handler", ) + returnWithOUTParameterErr = pgerror.New(pgcode.DatatypeMismatch, + "RETURN cannot have a parameter in function with OUT parameters", + ) ) diff --git a/pkg/sql/opt/optbuilder/routine.go b/pkg/sql/opt/optbuilder/routine.go index 4433130f541d..0eec089dfeb3 100644 --- a/pkg/sql/opt/optbuilder/routine.go +++ b/pkg/sql/opt/optbuilder/routine.go @@ -21,7 +21,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" plpgsql "github.com/cockroachdb/cockroach/pkg/sql/plpgsql/parser" "github.com/cockroachdb/cockroach/pkg/sql/sem/cast" - "github.com/cockroachdb/cockroach/pkg/sql/sem/plpgsqltree" + ast "github.com/cockroachdb/cockroach/pkg/sql/sem/plpgsqltree" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" "github.com/cockroachdb/cockroach/pkg/sql/types" @@ -73,8 +73,25 @@ func (b *Builder) buildUDF( if outScope != nil { outCol = b.synthesizeColumn(outScope, scopeColName(""), f.ResolvedType(), nil /* expr */, routine) } - } else if o.NamedReturnColumn != "" && b.insideDataSource { - outCol.name = scopeColName(tree.Name(o.NamedReturnColumn)) + } else if b.insideDataSource { + // When we have a single OUT parameter, it becomes the output column + // name. + var firstOutParamName tree.Name + var numOutParams int + for _, param := range o.RoutineParams { + if param.IsOutParam() { + numOutParams++ + if numOutParams == 1 { + firstOutParamName = param.Name + } + } + if numOutParams == 2 { + break + } + } + if numOutParams == 1 && firstOutParamName != "" { + outCol.name = scopeColName(firstOutParamName) + } } return b.finishBuildScalar(f, routine, inScope, outScope, outCol) @@ -293,22 +310,70 @@ func (b *Builder) buildRoutine( if err != nil { panic(err) } - // Add a RETURN NULL statement if the return type of the function is - // VOID and the last statement is not already a RETURN statement. This - // ensures that all possible code paths lead to a RETURN statement. - // TODO(#108298): There is a parsing bug that affects some PLpgSQL - // functions with VOID return types. - if rtyp.Family() == types.VoidFamily { + var hasReturn bool + if len(stmt.AST.Body) > 0 { lastStmt := stmt.AST.Body[len(stmt.AST.Body)-1] - if _, ok := lastStmt.(*plpgsqltree.Return); !ok { - stmt.AST.Body = append(stmt.AST.Body, &plpgsqltree.Return{Expr: tree.DNull}) + _, hasReturn = lastStmt.(*ast.Return) + } + if !hasReturn { + if rtyp.Family() == types.VoidFamily { + // Add a RETURN NULL statement if the return type of the + // function is VOID and the last statement is not already a + // RETURN statement. This ensures that all possible code paths + // lead to a RETURN statement. + // TODO(#108298): There is a parsing bug that affects some + // PLpgSQL functions with VOID return types. + stmt.AST.Body = append(stmt.AST.Body, &ast.Return{ + Expr: tree.DNull, + Implicit: true, + }) + } else { + // If the last statement is not a RETURN, and we have OUT + // parameters, then we need to add an implicit RETURN statement + // ourselves. + var exprs tree.Exprs + for _, param := range o.RoutineParams { + if param.IsOutParam() { + if param.Name != "" { + exprs = append(exprs, tree.NewUnresolvedName(string(param.Name))) + } else { + // TODO(100962): this logic will likely need to be + // changed. + exprs = append(exprs, tree.DNull) + } + } + } + if len(exprs) > 1 { + stmt.AST.Body = append(stmt.AST.Body, &ast.Return{ + Expr: &tree.Tuple{Exprs: exprs}, + Implicit: true, + }) + } else if len(exprs) == 1 { + stmt.AST.Body = append(stmt.AST.Body, &ast.Return{ + Expr: exprs[0], + Implicit: true, + }) + } } } + routineParams := make([]routineParam, 0, len(o.RoutineParams)) + for _, param := range o.RoutineParams { + // TODO(yuzefovich): can we avoid type resolution here? + typ, err := tree.ResolveType(b.ctx, param.Type, b.semaCtx.TypeResolver) + if err != nil { + panic(err) + } + routineParams = append(routineParams, routineParam{ + name: param.Name, + typ: typ, + class: param.Class, + }) + } + plBuilder := newPLpgSQLBuilder(b, def.Name, colRefs, routineParams, rtyp) + stmtScope := plBuilder.buildRootBlock(stmt.AST, bodyScope, routineParams) + finishResolveType(stmtScope) var expr memo.RelExpr var physProps *physical.Required - plBuilder := newPLpgSQLBuilder(b, def.Name, colRefs, o.Types.(tree.ParamTypes), rtyp) - stmtScope := plBuilder.buildRootBlock(stmt.AST, bodyScope) - finishResolveType(stmtScope) expr, physProps, isMultiColDataSource = b.finishBuildLastStmt(stmtScope, bodyScope, isSetReturning, f) body = []memo.RelExpr{expr} diff --git a/pkg/sql/opt/optbuilder/scope_column.go b/pkg/sql/opt/optbuilder/scope_column.go index 1074acd3c1e3..422ccf7a57d8 100644 --- a/pkg/sql/opt/optbuilder/scope_column.go +++ b/pkg/sql/opt/optbuilder/scope_column.go @@ -289,6 +289,9 @@ func scopeColName(name tree.Name) scopeColumnName { // is not empty. If the given name is empty, the returned scopeColumnName // represents an anonymous function argument that cannot be referenced, and it // will be added to the metadata with the descriptive name "arg". +// TODO(119502): unnamed parameters can be referenced via $i notation (for SQL +// routines only IN / INOUT parameters can be referenced this way, for PLpgSQL +// routines all parameters can). func funcParamColName(name tree.Name, ord int) scopeColumnName { alias := string(name) if alias == "" { diff --git a/pkg/sql/opt/optbuilder/srfs.go b/pkg/sql/opt/optbuilder/srfs.go index 4ebf2f450202..5ef4dbdbe801 100644 --- a/pkg/sql/opt/optbuilder/srfs.go +++ b/pkg/sql/opt/optbuilder/srfs.go @@ -197,8 +197,21 @@ func (b *Builder) finishBuildGeneratorFunction( ) (out opt.ScalarExpr) { lastAlias := inScope.alias if def.ReturnsRecordType { - if lastAlias == nil && !def.HasNamedReturnColumns { - panic(pgerror.New(pgcode.Syntax, "a column definition list is required for functions returning \"record\"")) + if lastAlias == nil { + var numOutParams int + for _, param := range def.RoutineParams { + if param.IsOutParam() { + numOutParams++ + } + if numOutParams == 2 { + break + } + } + // If we have at least two OUT parameters, they specify an implicit + // alias for the RECORD return type. + if numOutParams < 2 { + panic(pgerror.New(pgcode.Syntax, "a column definition list is required for functions returning \"record\"")) + } } } else if lastAlias != nil { // Non-record type return with a table alias that includes types is not diff --git a/pkg/sql/opt/testutils/testcat/function.go b/pkg/sql/opt/testutils/testcat/function.go index 060edf042129..fea3772cfcd1 100644 --- a/pkg/sql/opt/testutils/testcat/function.go +++ b/pkg/sql/opt/testutils/testcat/function.go @@ -82,9 +82,8 @@ func (tc *Catalog) CreateRoutine(c *tree.CreateRoutine) { } // Resolve the parameter names and types. - paramTypes := make(tree.ParamTypes, len(c.Params)) + signatureTypes := make(tree.ParamTypes, 0, len(c.Params)) var outParamTypes []*types.T - var firstOutParamName string var outParamNames []string for i := range c.Params { param := &c.Params[i] @@ -92,13 +91,15 @@ func (tc *Catalog) CreateRoutine(c *tree.CreateRoutine) { if err != nil { panic(err) } - paramTypes.SetAt(i, string(param.Name), typ) + if tree.IsParamIncludedIntoSignature(param.Class, c.IsProcedure) { + signatureTypes = append(signatureTypes, tree.ParamType{ + Name: string(param.Name), + Typ: typ, + }) + } if param.IsOutParam() { outParamTypes = append(outParamTypes, typ) paramName := string(param.Name) - if len(outParamNames) == 0 { - firstOutParamName = paramName - } if paramName == "" { paramName = fmt.Sprintf("column%d", len(outParamTypes)) } @@ -157,23 +158,20 @@ func (tc *Catalog) CreateRoutine(c *tree.CreateRoutine) { } tc.currUDFOid++ overload := &tree.Overload{ - Oid: tc.currUDFOid, - Types: paramTypes, - ReturnType: tree.FixedReturnType(retType), - Body: body, - Volatility: v, - CalledOnNullInput: calledOnNullInput, - HasNamedReturnColumns: len(outParamNames) > 1, - Language: language, - Type: routineType, + Oid: tc.currUDFOid, + Types: signatureTypes, + ReturnType: tree.FixedReturnType(retType), + Body: body, + Volatility: v, + CalledOnNullInput: calledOnNullInput, + Language: language, + Type: routineType, + RoutineParams: c.Params, } overload.ReturnsRecordType = types.IsRecordType(retType) if c.ReturnType != nil && c.ReturnType.SetOf { overload.Class = tree.GeneratorClass } - if len(outParamNames) == 1 { - overload.NamedReturnColumn = firstOutParamName - } prefixedOverload := tree.MakeQualifiedOverload("public", overload) def := &tree.ResolvedFunctionDefinition{ Name: name, diff --git a/pkg/sql/opt/testutils/testcat/testdata/udf b/pkg/sql/opt/testutils/testcat/testdata/udf index e39c49c71c4a..f5a80722591e 100644 --- a/pkg/sql/opt/testutils/testcat/testdata/udf +++ b/pkg/sql/opt/testutils/testcat/testdata/udf @@ -73,7 +73,7 @@ CREATE FUNCTION f_3_in_2_out(IN param1 INT, IN param2 INT, IN param3 INT, OUT pa exec-ddl SHOW CREATE FUNCTION f_3_in_2_out ---- -FUNCTION f_3_in_2_out(param1: int, param2: int, param3: int, param1: int, param2: int) -> tuple{int AS param1, int AS param2} [volatile] +FUNCTION f_3_in_2_out(param1: int, param2: int, param3: int) -> tuple{int AS param1, int AS param2} [volatile] └── SELECT (1, 1) exec-ddl @@ -83,7 +83,7 @@ CREATE FUNCTION f_out_int(OUT param INT) RETURNS INT LANGUAGE SQL AS 'SELECT 1' exec-ddl SHOW CREATE FUNCTION f_out_int ---- -FUNCTION f_out_int(param: int) -> int [volatile] +FUNCTION f_out_int() -> int [volatile] └── SELECT 1 exec-ddl @@ -93,5 +93,5 @@ CREATE FUNCTION f_default_names(OUT INT, OUT param2 INT, IN INT, OUT INT) RETURN exec-ddl SHOW CREATE FUNCTION f_default_names ---- -FUNCTION f_default_names(: int, param2: int, : int, : int) -> tuple{int AS column1, int AS param2, int AS column3} [volatile] +FUNCTION f_default_names(: int) -> tuple{int AS column1, int AS param2, int AS column3} [volatile] └── SELECT (1, 2, 3) diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/references.go b/pkg/sql/schemachanger/scexec/scmutationexec/references.go index 04228d8b6c81..19a541411d05 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/references.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/references.go @@ -603,11 +603,9 @@ func (i *immediateVisitor) SetObjectParentID(ctx context.Context, op scop.SetObj IsProcedure: t.IsProcedure(), } for _, p := range t.Params { - if !tree.IsInParamClass(funcdesc.ToTreeRoutineParamClass(p.Class)) { - // Only IN parameters are included into the signature. - continue + if tree.IsParamIncludedIntoSignature(funcdesc.ToTreeRoutineParamClass(p.Class), ol.IsProcedure) { + ol.ArgTypes = append(ol.ArgTypes, p.Type) } - ol.ArgTypes = append(ol.ArgTypes, p.Type) } sc.AddFunction(obj.GetName(), ol) } diff --git a/pkg/sql/sem/plpgsqltree/statements.go b/pkg/sql/sem/plpgsqltree/statements.go index 88f57ddae1c3..b6f7f9d442dc 100644 --- a/pkg/sql/sem/plpgsqltree/statements.go +++ b/pkg/sql/sem/plpgsqltree/statements.go @@ -791,6 +791,9 @@ type Return struct { StatementImpl Expr Expr RetVar Variable + // Implicit is set if this Return statement was not originally in the body + // and was added by us. + Implicit bool } func (s *Return) CopyNode() *Return { diff --git a/pkg/sql/sem/tree/create_routine.go b/pkg/sql/sem/tree/create_routine.go index 9d3ad4ae60dc..2b4f7cf4ba33 100644 --- a/pkg/sql/sem/tree/create_routine.go +++ b/pkg/sql/sem/tree/create_routine.go @@ -407,6 +407,15 @@ func IsInParamClass(class RoutineParamClass) bool { } } +// IsParamIncludedIntoSignature returns whether the parameter of the given class +// is included into the signature of the routine (either a function, when +// isProcedure is false, or a procedure, when isProcedure is true). +func IsParamIncludedIntoSignature(class RoutineParamClass, isProcedure bool) bool { + // For procedures all parameters are included into the signature, for UDFs - + // only IN / INOUT parameters. + return isProcedure || IsInParamClass(class) +} + // IsOutParamClass returns true if the given parameter class specifies an output // parameter (i.e. either OUT or INOUT). func IsOutParamClass(class RoutineParamClass) bool { @@ -502,6 +511,8 @@ func (node RoutineObj) SignatureTypes( typs = make([]*types.T, 0, len(node.Params)) for _, arg := range node.Params { if arg.IsInParam() { + // TODO(100405): we might need to include all parameters for + // procedures here. typ, err := ResolveType(ctx, arg.Type, res) if err != nil { return nil, err diff --git a/pkg/sql/sem/tree/overload.go b/pkg/sql/sem/tree/overload.go index 2cb236958ecf..f788d5c9b572 100644 --- a/pkg/sql/sem/tree/overload.go +++ b/pkg/sql/sem/tree/overload.go @@ -262,18 +262,18 @@ type Overload struct { // in a Schema descriptor, which means that the full UDF descriptor need to be // fetched to get more info, e.g. function Body. UDFContainsOnlySignature bool - // NamedReturnColumn is non-empty when a user-defined function returns a - // single column of non-RECORD type and has named OUT parameter. - NamedReturnColumn string - // HasNamedReturnColumns is set when a user-defined function has multiple - // OUT parameters that specify an implicit alias for the RECORD return type. - HasNamedReturnColumns bool // Version is the descriptor version of the descriptor used to construct // this version of the function overload. Only used for UDFs. Version uint64 // Language is the function language that was used to define the UDF. // This is currently either SQL or PL/pgSQL. Language RoutineLanguage + // RoutineParams contains all parameter information of the routine. + // + // Note that unlike Types (which only contains input parameters and defines + // the signature of the function), RoutineParams contains all parameters as + // well as their class. + RoutineParams RoutineParams } // params implements the overloadImpl interface. @@ -460,12 +460,6 @@ func (p ParamTypes) GetAt(i int) *types.T { return p[i].Typ } -// SetAt is part of the TypeList interface. -func (p ParamTypes) SetAt(i int, name string, t *types.T) { - p[i].Name = name - p[i].Typ = t -} - // Length is part of the TypeList interface. func (p ParamTypes) Length() int { return len(p) diff --git a/pkg/sql/show_test.go b/pkg/sql/show_test.go index 8fe3e642a5cd..214d8e61fc0a 100644 --- a/pkg/sql/show_test.go +++ b/pkg/sql/show_test.go @@ -1052,7 +1052,8 @@ func TestShowSessionPrivileges(t *testing.T) { } } -// TestShowRedactedActiveStatements tests the crdb_internal.cluster_queries table for system permissions. +// TestShowRedactedActiveStatements tests the crdb_internal.cluster_queries +// table for system permissions. func TestShowRedactedActiveStatements(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1077,16 +1078,18 @@ func TestShowRedactedActiveStatements(t *testing.T) { _ = sqlDBroot.Exec(t, `GRANT SYSTEM VIEWACTIVITYREDACTED TO bothperms`) type user struct { - username string - canViewTable bool // Can the user view the `cluster_queries` table? - isQueryRedacted bool // Is the user's query redacted? - sqlRunner *sqlutils.SQLRunner + username string + canViewOtherRows bool // Can the user view other users' rows in the table? + isQueryRedacted bool // Are the other userss queries redacted? + sqlRunner *sqlutils.SQLRunner } - // A user with no permissions should not see the table. A user with only - // VIEWACTIVITY should be able to see the whole query. A user with only - // VIEWACTIVITYREDACTED should see a redacted query. A user with both should - // see the redacted query, as VIEWACTIVITYREDACTED takes precedence. + // A user with no permissions should only be able to see their own rows + // in the table. + // A user with only VIEWACTIVITY should be able to see the whole query. + // A user with only VIEWACTIVITYREDACTED should see a redacted query. + // A user with both should see the redacted query, as VIEWACTIVITYREDACTED + // takes precedence. users := []user{ {"onlyviewactivityredacted", true, true, nil}, {"onlyviewactivity", true, false, nil}, @@ -1125,10 +1128,10 @@ func TestShowRedactedActiveStatements(t *testing.T) { // Wait for the start signal. <-startSignal - selectQuery := `SELECT query FROM [SHOW CLUSTER QUERIES] WHERE query LIKE 'SELECT pg_sleep%'` + selectRootQuery := `SELECT query FROM [SHOW CLUSTER QUERIES] WHERE query LIKE 'SELECT pg_sleep%' AND user_name = 'root'` testutils.SucceedsSoon(t, func() error { - rows := sqlDBroot.Query(t, selectQuery) + rows := sqlDBroot.Query(t, selectRootQuery) defer rows.Close() count := 0 for rows.Next() { @@ -1149,41 +1152,221 @@ func TestShowRedactedActiveStatements(t *testing.T) { for _, u := range users { t.Run(u.username, func(t *testing.T) { - // Make sure that if the user can't view the table, they get an error. - if !u.canViewTable { - u.sqlRunner.ExpectErr(t, "does not have VIEWACTIVITY or VIEWACTIVITYREDACTED privilege", selectQuery) - } else { - rows := u.sqlRunner.Query(t, selectQuery) - defer rows.Close() - if err := rows.Err(); err != nil { + rootRows := u.sqlRunner.Query(t, selectRootQuery) + defer func() { require.NoError(t, rootRows.Close()) }() + + count := 0 + for rootRows.Next() { + count++ + + var query string + if err := rootRows.Scan(&query); err != nil { t.Fatal(err) } - count := 0 - for rows.Next() { - count++ - var query string - if err := rows.Scan(&query); err != nil { - t.Fatal(err) + t.Log(query) + // Make sure that if the user is supposed to see a redacted query, they do. + if u.isQueryRedacted { + if !strings.HasPrefix(query, "SELECT pg_sleep(_)") { + t.Fatalf("Expected `SELECT pg_sleep(_)`, got %s", query) + } + // Make sure that if the user is supposed to see the full query, they do. + } else { + if !strings.HasPrefix(query, "SELECT pg_sleep(30)") { + t.Fatalf("Expected `SELECT pg_sleep(30)`, got %s", query) } + } + } + if u.canViewOtherRows { + require.Equalf(t, 1, count, "expected 1 row, got %d", count) + } else { + require.Equalf(t, 0, count, "expected 0 rows, got %d", count) + } - t.Log(query) - // Make sure that if the user is supposed to see a redacted query, they do. - if u.isQueryRedacted { - if !strings.HasPrefix(query, "SELECT pg_sleep(_)") { - t.Fatalf("Expected `SELECT pg_sleep(_)`, got %s", query) - } - // Make sure that if the user is supposed to see the full query, they do. - } else { - if !strings.HasPrefix(query, "SELECT pg_sleep(30)") { - t.Fatalf("Expected `SELECT pg_sleep(30)`, got %s", query) - } + selectOwnQuery := fmt.Sprintf( + `SELECT query FROM [SHOW CLUSTER QUERIES] WHERE query LIKE 'SELECT query FROM%%' AND user_name = '%s'`, + u.username, + ) + ownRows := u.sqlRunner.Query(t, selectOwnQuery) + defer func() { require.NoError(t, ownRows.Close()) }() + + count = 0 + for ownRows.Next() { + count++ + + var query string + if err := ownRows.Scan(&query); err != nil { + t.Fatal(err) + } + + t.Log(query) + // Any user can always see their own unredacted queries. + if !strings.Contains(query, fmt.Sprintf("user_name = '%s'", u.username)) { + t.Fatalf("Expected unredacted query, got %s", query) + } + } + require.Equalf(t, 1, count, "expected 1 row, got %d", count) + }) + } + + cancel() + <-waiter +} + +// TestShowRedactedSessions tests the crdb_internal.cluster_sessions +// table for system permissions. +func TestShowRedactedSessions(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + params, _ := createTestServerParams() + params.Insecure = true + ctx, cancel := context.WithCancel(context.Background()) + s, rawSQLDBroot, _ := serverutils.StartServer(t, params) + sqlDBroot := sqlutils.MakeSQLRunner(rawSQLDBroot) + defer s.Stopper().Stop(context.Background()) + + // Create four users: one with no special permissions, one with the + // VIEWACTIVITY role option, one with VIEWACTIVITYREDACTED option, + // and one with both permissions. + _ = sqlDBroot.Exec(t, `CREATE USER noperms`) + _ = sqlDBroot.Exec(t, `CREATE USER onlyviewactivity`) + _ = sqlDBroot.Exec(t, `CREATE USER onlyviewactivityredacted`) + _ = sqlDBroot.Exec(t, `CREATE USER bothperms`) + _ = sqlDBroot.Exec(t, `GRANT SYSTEM VIEWACTIVITY TO onlyviewactivity`) + _ = sqlDBroot.Exec(t, `GRANT SYSTEM VIEWACTIVITYREDACTED TO onlyviewactivityredacted`) + _ = sqlDBroot.Exec(t, `GRANT SYSTEM VIEWACTIVITY TO bothperms`) + _ = sqlDBroot.Exec(t, `GRANT SYSTEM VIEWACTIVITYREDACTED TO bothperms`) + + type user struct { + username string + canViewOtherRows bool // Can the user view other users' rows in the table? + isQueryRedacted bool // Are the other userss queries redacted? + sqlRunner *sqlutils.SQLRunner + } + + // A user with no permissions should only be able to see their own rows + // in the table. + // A user with only VIEWACTIVITY should be able to see the whole query. + // A user with only VIEWACTIVITYREDACTED should see a redacted query. + // A user with both should see the redacted query, as VIEWACTIVITYREDACTED + // takes precedence. + users := []user{ + {"onlyviewactivityredacted", true, true, nil}, + {"onlyviewactivity", true, false, nil}, + {"noperms", false, false, nil}, + {"bothperms", true, true, nil}, + } + for i, tc := range users { + pgURL := url.URL{ + Scheme: "postgres", + User: url.User(tc.username), + Host: s.AdvSQLAddr(), + RawQuery: "sslmode=disable", + } + db, err := gosql.Open("postgres", pgURL.String()) + if err != nil { + t.Fatal(err) + } + defer db.Close() + users[i].sqlRunner = sqlutils.MakeSQLRunner(db) + + // Ensure the session is open. + users[i].sqlRunner.Exec(t, `SELECT version()`) + } + + // Run a long-running sleep query in the background. + startSignal := make(chan struct{}) + waiter := make(chan struct{}) + go func() { + // Signal that we have started the query. + close(startSignal) + _, _ = rawSQLDBroot.ExecContext(ctx, `SELECT pg_sleep(30)`) + // Signal that we have finished the query. + close(waiter) + }() + + // Wait for the start signal. + <-startSignal + + selectRootQuery := `SELECT active_queries FROM [SHOW CLUSTER SESSIONS] WHERE active_queries LIKE 'SELECT pg_sleep%' AND user_name = 'root'` + + testutils.SucceedsSoon(t, func() error { + rows := sqlDBroot.Query(t, selectRootQuery) + defer rows.Close() + count := 0 + for rows.Next() { + count++ + var query string + if err := rows.Scan(&query); err != nil { + return err + } + if query != "SELECT pg_sleep(30)" { + return errors.Errorf("Expected `SELECT pg_sleep(30)`, got %s", query) + } + } + if count != 1 { + return errors.Errorf("expected 1 row, got %d", count) + } + return nil + }) + + for _, u := range users { + t.Run(u.username, func(t *testing.T) { + rootRows := u.sqlRunner.Query(t, selectRootQuery) + defer func() { require.NoError(t, rootRows.Close()) }() + + count := 0 + for rootRows.Next() { + count++ + + var query string + if err := rootRows.Scan(&query); err != nil { + t.Fatal(err) + } + + t.Log(query) + // Make sure that if the user is supposed to see a redacted query, they do. + if u.isQueryRedacted { + if !strings.HasPrefix(query, "SELECT pg_sleep(_)") { + t.Fatalf("Expected `SELECT pg_sleep(_)`, got %s", query) + } + // Make sure that if the user is supposed to see the full query, they do. + } else { + if !strings.HasPrefix(query, "SELECT pg_sleep(30)") { + t.Fatalf("Expected `SELECT pg_sleep(30)`, got %s", query) } } - if count != 1 { - t.Fatalf("expected 1 row, got %d", count) + } + if u.canViewOtherRows { + require.Equalf(t, 1, count, "expected 1 row, got %d", count) + } else { + require.Equalf(t, 0, count, "expected 0 rows, got %d", count) + } + + selectOwnQuery := fmt.Sprintf( + `SELECT active_queries FROM [SHOW CLUSTER SESSIONS] WHERE active_queries LIKE 'SELECT active_queries FROM%%' AND user_name = '%s'`, + u.username, + ) + ownRows := u.sqlRunner.Query(t, selectOwnQuery) + defer func() { require.NoError(t, ownRows.Close()) }() + + count = 0 + for ownRows.Next() { + count++ + + var query string + if err := ownRows.Scan(&query); err != nil { + t.Fatal(err) + } + + t.Log(query) + // Any user can always see their own unredacted queries. + if !strings.Contains(query, fmt.Sprintf("user_name = '%s'", u.username)) { + t.Fatalf("Expected unredacted query, got %s", query) } } + require.Equalf(t, 1, count, "expected 1 row, got %d", count) }) }