Skip to content

Specify storage inheritance for FILE self-references - #603

Open
alkis wants to merge 10 commits into
apache:masterfrom
alkis:file-self-reference-compression
Open

Specify storage inheritance for FILE self-references#603
alkis wants to merge 10 commits into
apache:masterfrom
alkis:file-self-reference-compression

Conversation

@alkis

@alkis alkis commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Specify storage inheritance semantics for FILE self-references:

  • require an inline field for schemas permitting self-references
  • inherit compression and encryption from the corresponding inline column chunk
  • define each referenced range as an independent compression/encryption block
  • define offset and size over the stored representation
  • require Parquet-mediated resolution of stored bytes
  • define modular-encryption serialization and a page-independent AAD identity
  • leave external references unaffected

Discussion: https://lists.apache.org/thread/zrzc7t9fccg92rx3h4fw3ndw3bdo5xr7

@alkis alkis changed the title Specify compression for FILE self-references Specify storage inheritance for FILE self-references Jul 30, 2026
Comment thread LogicalTypes.md
Comment thread LogicalTypes.md
Comment on lines +773 to +774
compressed block or the uncompressed bytes. For a compressed block, the complete range
is supplied to the codec, and its decompressed output is the resolved value.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also store the decompressed size somewhere?

@alkis alkis Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have thought about this but it complicates the format - we will need framing for the entry. For some compressors this exists already (Snappy, optionally ZSTD), for the rest we can require a dynamically sized decompressor output as discussed here https://lists.apache.org/thread/rfz37kt9bdjw6m7o6mwf8f7nr712c0m8. If there is strong push for uncompressed size we can add a header but that's a larger change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we cal leave this open to be specified later if needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I made that explicit in the spec rather than leaving it unsaid: the decompressed size is not stored, readers rely on the codec's framing where it provides one or decompress into a dynamically sized buffer, and a future revision may add an explicit decompressed size.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will likely ever be needed. It's trivial to workaround the framing issues and working with File should indicate that there is an expectation that large values may be present. Readers need to be very permissive if they're going to support this type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and that is the conclusion I came to as well. The paragraph no longer speculates about a future field — per your other comment it just states that the decompressed size is not stored and that readers use the codec framing or a dynamically sized buffer.

@rok, flagging since removing the sentence also removes the "may be specified later" hedge you asked for. The substance you wanted is unchanged: nothing here precludes adding an explicit decompressed size in a later revision, we just are not advertising it in the spec text.

Comment thread Encryption.md Outdated
Comment on lines +319 to +321
For a self-reference, `offset` points to the beginning of the 4-byte length and `size`
is the size of the complete encrypted module, including the length, nonce, ciphertext,
and GCM tag when present.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4-byte length limits us to 2GiB payload. Are we expecting use cases with greater size requirements?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect 2GiB payloads to be in their own external file reference not embedded inside parquet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And that means we can't use parquet modular encryption, right? Should we note that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opposite — with this PR self-references are modular encryption modules (module type 10, keyed on the inline column key), so modular encryption works for them. This PR removes the prohibition that the merged spec had.

Two limits are worth noting and are now stated:

  • The encryption buffer must fit the 4-byte length field, so an encrypted self-reference is capped at 2 GiB as for any other module. Larger values use an external reference.
  • The AAD includes the row group and column ordinals, so an encrypted stored representation is bound to one column chunk. A writer must not share one encrypted byte range between column chunks.

Comment thread Encryption.md
| OffsetIndex | yes | yes (7) | yes | yes | no | no |
| BloomFilter Header | yes | yes (8) | yes | yes | no | no |
| BloomFilter Bitset | yes | yes (9) | yes | yes | no | no |
| Self-Reference | yes | yes (10) | yes | yes | no | yes |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Page ordinal is not used here? When only a few pages are required to read, we still need to count number of values of skipped pages in order to decrypt a self-referenced value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that a derived ordinal is wrong for exactly this reason. I switched the AAD to key on the self-reference offset instead. The offset is a field of the FILE value being read, so the reader has it in hand and never counts values in pages it skips.

Page ordinal would also work for a reader — you know the ordinal of the page you are decoding — but it ties the out-of-band range to page layout, which the rest of this PR deliberately avoids: compression and encryption are properties of the column chunk, not of the page a value happens to land in. Keying on offset keeps the stored representation independent of page boundaries, and the offset already uniquely identifies it within the file.

Key the self-reference AAD on `offset` rather than a derived ordinal, so a
reader can resolve a self-reference without decoding the pages it skips.

State that the encryption buffer of a self-reference must fit the 4-byte
length field, and that larger values must use an external reference.

State that the decompressed size is not stored and how readers obtain it.

State that an encrypted stored representation is bound to a single column
chunk and must not be shared between column chunks.
Comment thread LogicalTypes.md Outdated
A value of 0 indicates an empty stored representation. `size` must be set whenever
`offset` is set. It may be omitted only for a whole-file external reference (`uri` set,
`offset` not set), in which case the range runs to the end of the referenced file.
Because a self-reference always sets `offset`, it always sets `size` as well.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Because a self-reference always sets `offset`, it always sets `size` as well.
Because a self-reference always sets `offset`, it must always set `size` as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in aac4d72. It also lines up with the validation bullet further down, which already stated this with "must".

Comment thread LogicalTypes.md Outdated
Comment on lines +763 to +764
so all self-references of a column chunk share them regardless of the page a value is
stored in.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
so all self-references of a column chunk share them regardless of the page a value is
stored in.
so all self-references of a column chunk share them regardless of where in the file the referenced value is
stored.

This seems to indicate that the value is stored in a page, but it's actually outside the page data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — applied in aac4d72. You are right that the old wording implied the payload lives in a page, which is exactly what it does not do. The point I wanted to make is that the settings come from the column chunk, so your phrasing says it without the misleading implication.

Comment thread LogicalTypes.md Outdated

The decompressed size of a self-reference is not stored. Readers must rely on the
framing of the codec where it provides one, or decompress into a dynamically sized
buffer. A future revision of this specification may add an explicit decompressed size.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
buffer. A future revision of this specification may add an explicit decompressed size.
buffer.

I'd prefer not to include forward-looking statements.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in aac4d72 — the forward-looking sentence is gone. The paragraph now just states that the decompressed size is not stored and how a reader copes.

Comment thread LogicalTypes.md Outdated
construction.

A self-reference identifies a stored representation, not necessarily the resolved
bytes. Consumers must use a Parquet reader to resolve a self-reference; copying

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consumers must use a Parquet reader to resolve a self-reference

I think this is an unnecessary MUST statement. I think it's fine to just say: "copying [offset, offset + size) directly may return compressed or encrypted data".

The intent is to describe what needs to be done to correctly interpret the bytes, not enforce who does it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in aac4d72. The sentence now reads "Copying [offset, offset + size) directly may return compressed or encrypted data." — describing the consequence rather than mandating who does the resolution.

Comment thread LogicalTypes.md Outdated
Comment on lines +783 to +784
encryption. If the column chunk is not encrypted, its self-references are not
encrypted. An encrypted self-reference is limited to 2 GiB by the length field of the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to call out explicitly here that for encrypted columns, the offset+size represents the post-encrypted size (similar to the statement we make around line 772).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in aac4d72: "For an encrypted self-reference, offset and size identify the encrypted module, so size is the size after compression and encryption."

One caveat on precision: size also covers the 4-byte length prefix and the nonce, which are not themselves products of compression or encryption, so "after compression and encryption" is a shorthand. Encryption.md has the exact layout — offset points at the 4-byte length, and size spans the length, nonce, ciphertext, and GCM tag when present. Happy to replace the shorthand with a pointer to that layout if you would rather the normative statement live in one place.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took my own suggestion in ad3d1fa. The shorthand is replaced with a pointer:

For an encrypted self-reference, offset and size identify the encrypted module, whose exact extent is defined in Encrypted module serialization.

That keeps the normative definition of the extent in Encryption.md section 5.1 — offset at the 4-byte length, size spanning length, nonce, ciphertext, and GCM tag when present — instead of restating a lossy version of it here.

alkis added 3 commits August 6, 2026 18:27
Align the wording with the validation rules, which state the requirement
normatively.

Co-authored-by: Isaac
Describe self-reference storage as being anywhere in the file rather than
in a page, since the referenced bytes are outside the page data.

Drop the forward-looking note about a possible decompressed size field.

State that `offset` and `size` cover the encrypted module for an encrypted
self-reference, as already stated for the unencrypted case.

Describe rather than mandate how the stored bytes are interpreted.

Co-authored-by: Isaac
The previous wording described `size` as the size after compression and
encryption, which omits the length prefix and nonce that the range also
covers. Reference the layout in Encryption.md so the extent is defined in
one place.

Co-authored-by: Isaac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants