Skip to content

Covering index scans cannot produce a partial record when a field is required (not nullable). #4592

Description

@hatyo

A covering index scan decodes an index entry into a partial record: the columns the index carries are set, and every other field of the record type is left absent. That only works while absence is representable for every field. A not-nullable field makes it unrepresentable; the decoder has no value to supply, and the type does not admit that the field may be missing, therefore supporting not-nullable columns is blocked until a covering scan can express what it actually returns.

Example

Setup:

CREATE TABLE T(id BIGINT, a BIGINT NOT NULL, c BIGINT, d BIGINT, PRIMARY KEY(id));
CREATE INDEX mv AS SELECT c, d FROM T ORDER BY c, d;

The query:

SELECT c, d FROM T WHERE c = 20;   -- planned as a covering scan over mv

The plan is a covering scan whose copiers fill three fields:

COVERING(MV [EQUALS promote(@c9 AS LONG)] -> [C: KEY:[0], D: KEY:[1], ID: KEY:[3]]) | MAP (_.C AS C, _.D AS D)

and the entry against the record type looks like this:

index entry :  KEY( c, d, <record type>, id )     VALUE( )
record type :  id, a, c, d
filled      :  c, d, id
absent      :  a          <-- NOT NULL, and nothing can fill it

a is not carried by the index, so no copier fills it, yet the operator still declares its result type to be the full record type, in which a cannot be absent. IndexKeyValueToPartialRecord.toRecordInternal already anticipates the failure:

if (isRequired) {
    // If any of the copiers refused since they were asked to copy a null into a required field, this following
    // build call will fail with an exception.
    return recordBuilder.build();
}

A copier refuses when asked to put a null into a required field (FieldCopier.copy returns !fieldDescriptor.isRequired()), and a field nobody fills is simply never set, so build() fails on an uninitialized message. Nullability in the type system follows the same fact:
Type.Record.Field.fromDescriptor derives it as !fieldDescriptor.isRequired().

This effectively means that we currently can not have a covering index scan when one of the uncovered fields is constrained to be not-null, or, in protobuf parlance, required.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions