From fe5d17a5445131a97e197bad4039c144831c62b6 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Wed, 6 Dec 2023 17:20:05 +0000 Subject: [PATCH] sql: prevent creating indexes on arrays of non-indexable types Previously, it was possible to circumvent the check performed by `ColumnTypeIsIndexable` and `ColumnTypeIsInvertedIndexable` by wrapping a type in an array. Doing so would violate some invariants across the system and lead to undefined behavior. This commit updates both `ColumnTypeIsIndexable` and `ColumnTypeIsInvertedIndexable` to unwrap array types, as necessary, to prevent invalid indexes from being created. Fixes: #115701 Epic: none Release note (bug fix): Standard and inverted indexes may no longer be created on REFCURSOR[]s columns. REFCURSOR columns themselves are not indexable. --- .../testdata/logic_test/refcursor | 43 +++++++++++++++++++ pkg/sql/catalog/colinfo/col_type_info.go | 13 +++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/ccl/logictestccl/testdata/logic_test/refcursor b/pkg/ccl/logictestccl/testdata/logic_test/refcursor index d6a72006de23..74569a33c7c2 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/refcursor +++ b/pkg/ccl/logictestccl/testdata/logic_test/refcursor @@ -596,6 +596,49 @@ CREATE INDEX ON t112099_no_index (r); statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor and thus is not indexable CREATE INDEX ON t112099_no_index (x, r, y); +# Regression test for #115701 - REFCURSOR[] is not a valid index column. +subtest refcursor[]_array_index + +statement ok +CREATE TABLE t115701_no_index (x INT NOT NULL, y TEXT NOT NULL, r REFCURSOR[] NOT NULL); + +# REFCURSOR[] is not allowed in a primary key. +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE TABLE t115701 (r REFCURSOR[] PRIMARY KEY); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE TABLE t115701 (x INT, y TEXT, r REFCURSOR[], PRIMARY KEY (x, r, y)); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +ALTER TABLE t115701_no_index ADD PRIMARY KEY (r); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +ALTER TABLE t115701_no_index ADD PRIMARY KEY (x, r, y); + +# REFCURSOR[] is not allowed in a secondary index. +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE TABLE t115701 (r REFCURSOR[], INDEX (r)); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE TABLE t115701 (x INT, y TEXT, r REFCURSOR[], INDEX (x, r, y)); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE INDEX ON t115701_no_index (r); + +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE INDEX ON t115701_no_index (x, r, y); + +statement error pgcode 0A000 pq: column r of type refcursor\[\] is not allowed as the last column in an inverted index +CREATE INDEX ON t115701_no_index USING GIN (r) + +skipif config local-legacy-schema-changer +statement error pgcode 0A000 pq: unimplemented: column r is of type refcursor\[\] and thus is not indexable +CREATE INDEX ON t115701_no_index USING GIN (x, r, y gin_trgm_ops) + +onlyif config local-legacy-schema-changer +statement error pgcode 0A000 pq: column r of type refcursor\[\] is only allowed as the last column in an inverted index +CREATE INDEX ON t115701_no_index USING GIN (x, r, y gin_trgm_ops) + # REFCURSOR is allowed as a STORING column. statement ok CREATE TABLE t112099 (x INT, r REFCURSOR, INDEX (x) STORING (r)); diff --git a/pkg/sql/catalog/colinfo/col_type_info.go b/pkg/sql/catalog/colinfo/col_type_info.go index 91b047e11cf3..b75419abe4a0 100644 --- a/pkg/sql/catalog/colinfo/col_type_info.go +++ b/pkg/sql/catalog/colinfo/col_type_info.go @@ -150,9 +150,18 @@ func ValidateColumnDefType(ctx context.Context, version clusterversion.Handle, t // ColumnTypeIsIndexable returns whether the type t is valid as an indexed column. func ColumnTypeIsIndexable(t *types.T) bool { + // NB: .IsAmbiguous checks the content type of array types. if t.IsAmbiguous() || t.Family() == types.TupleFamily || t.Family() == types.RefCursorFamily { return false } + + // If the type is an array, check its content type as well. + if unwrapped := t.ArrayContents(); unwrapped != nil { + if unwrapped.Family() == types.TupleFamily || unwrapped.Family() == types.RefCursorFamily { + return false + } + } + // Some inverted index types also have a key encoding, but we don't // want to support those yet. See #50659. return !MustBeValueEncoded(t) && !ColumnTypeIsOnlyInvertedIndexable(t) @@ -162,7 +171,9 @@ func ColumnTypeIsIndexable(t *types.T) bool { // using an inverted index. func ColumnTypeIsInvertedIndexable(t *types.T) bool { switch t.Family() { - case types.JsonFamily, types.ArrayFamily, types.StringFamily: + case types.ArrayFamily: + return t.ArrayContents().Family() != types.RefCursorFamily + case types.JsonFamily, types.StringFamily: return true } return ColumnTypeIsOnlyInvertedIndexable(t)