Skip to content

21 more catalog scans pass InvalidOid where their key is a prefix of an existing index #1207

Description

@OffgridwithJD

What

src/columnar_metadata.c makes 44 systable_beginscan calls. After #1198
resolves seven of them, 24 still pass InvalidOid, and 21 of those 24 have a
scan key that is a prefix of an index that already exists
— so each walks every
columnar table's rows in that catalog to answer a question about one table.

#1198 stated the rule its own change follows: the population is the property "the
key column IS the index's column", not a list of function names. This issue is
the rest of that population.

The 21

Line numbers are on 6cb9f9a so the counts can be checked rather than believed.

catalog key columns index it is a prefix of sites lines
free_space storage_id free_space_pkey (storage_id, file_offset) 6 747, 903, 1041, 1102, 1147, 1315
storage storage_id storage_pkey (storage_id) 6 1896, 1934, 1966, 2256, 2328, 2399
projection_declaration rel projection_declaration_pkey (rel, name) 4 3699, 3840, 3915, 3955
row_group storage_id row_group_pkey (storage_id, group_number) 3 488, 567, 1020
row_group storage_id, group_number row_group_pkey exactly 2 671, 1596

6 + 6 + 4 + 3 + 2 = 21.

The other three InvalidOid sites are not in the list: storage.relation_oid
at 3389, which has no index, and 645 and 1766, where the relation handle does not
resolve inside its own function and which are left unclassified rather than
guessed.

How it was derived, and why the method is the point

The relation handle is the ground truth. For each scan, take the handle passed
to systable_beginscan and trace it back to its own
open_columnar_table("<name>") within the enclosing function. Then collect that
scan's ScanKeyInit(&key[N], Anum_...) statements, ordered by N, stopping at
the previous systable_beginscan.

Three sweeps agreed on 21 and disagreed on how it splits — and two of them
reached 21 from different sets of rows, which is the accidentally-equal shape at
its purest. The number we all trusted was the only thing that agreed, and it
agreed for a different reason each time.

The discriminating site is 1041. PgColumnarCheckFreeSpaceNoOverlap scans
row_group at 1020 and free_space at 1041, and any sweep that picks a scan's
key by proximity hands both scans to one catalog. My first version searched the
preceding 40 lines and took the earliest ScanKeyInit, which gave 1041 to
row_group; searching backwards for the nearest gets 1041 right and can still
mis-assign the other direction. Only the handle settles it.

Two further traps this sweep had to survive, both caught by @jdatcmd:

  • native_storage is not a table. pgcolumnar--1.0-alpha5.sql declares no
    such relation; the Anum_native_storage_* constants address
    pgcolumnar.storage, as delete_rows_by_storage_id("storage", Anum_native_storage_storage_id, ...) shows. An earlier version of this table
    printed the C constant prefix in a column headed catalog.
  • A count of the string is not a count of the calls. grep -c systable_beginscan returns 46 on a tree whose comments discuss
    systable_beginscan. The call count is 44.

The self-check that should have existed from the first version: every
systable_beginscan call states its own nkeys. Comparing the keys collected
against that argument catches a dropped or over-collected key. On the table above
it reports 0 mismatches across all 24 sites.

What this issue does NOT claim

None of the 21 is measured, so none is called a defect. #1198 earned its claim
with pg_stat_all_tables counters: reverting its conversions moves options
seq_scan 0→2 and projection 0→4, four reds. Nothing equivalent has been run
for these. Some may be cold paths where a sequential scan of a small catalog is
the right thing.

Suggested next step

One catalog at a time, and establish the cost before the conversion — the
pg_stat_reset() / pg_stat_force_next_flush() probe in
test/catalog_plan_index.sh is the seam that answers it. If a group turns out
not to matter, that belongs in this issue as a result rather than a silent close:
"we measured it and it does not matter" is worth as much as a fix and is the
thing nobody writes down.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions