Skip to content

Introduces a new LogicalType: FILE#585

Open
brkyvz wants to merge 10 commits into
apache:masterfrom
brkyvz:fileType
Open

Introduces a new LogicalType: FILE#585
brkyvz wants to merge 10 commits into
apache:masterfrom
brkyvz:fileType

Conversation

@brkyvz

@brkyvz brkyvz commented Jun 9, 2026

Copy link
Copy Markdown

Rationale for this change

Introduces a new type called File as a typed FileReference. The design document is here.

The motivation is as follows:

Unstructured data ingestion is getting extremely popular with the advances in Generative AI. 
Today, our only means of dealing with unstructured data is to store it as a binary blob inside Parquet, 
or point to files that exist in some object store with a string. These solutions fail to address these use 
cases, because of scalability, usability, and governance issues.

We would like to introduce a new logical type annotation in Parquet called “File” for storing a struct that 
contains a path reference to a file with additional metadata. This reference may be to a file that exists 
(or expected to exist) in storage at a given path. We’d like to define the minimum required list of fields 
that would allow a client to correctly read the referenced data. Any additional metadata can be optionally 
stored by engines and table formats as necessary adjacent to this type. 

What changes are included in this PR?

Introduces the specification for FileType.

Do these changes have PoC implementations?

Yes:

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated

@emkornfield emkornfield left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks reasonable to me a few minor comments for clarification.

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just a few questions I have after reviewing the Rust implementation.

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
Comment on lines +647 to +648
The annotated group must contain the following fields, identified by name. Field IDs
may also be used for projection:

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.

Suggested change
The annotated group must contain the following fields, identified by name. Field IDs
may also be used for projection:
The annotated group must contain the following fields, identified by name (not by order).
Field IDs (if exist) may also be used for projection:

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
##### size

The length of the content in bytes. Must be zero or a positive integer if provided.
A value of 0 indicates an empty file. If not provided, the length of the referenced

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.

Do we want to advise the reader to ignore negative value here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

IMO we should specify that readers error on invalid values, not silently ignore

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We can't require readers to fail, we can only say what it means when the length is -1. Failure is a responsibility of whatever uses this data, and it's even hard to require that component to fail because users don't want their query to fail for one bad row. They expect reasonable fallback behavior.

Also, does 0 indicate an empty file or does it indicate 0-length content? With offset, I think this indicates that the content length is 0, not that the underlying file is empty.

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
A value of 0 indicates an empty file. If not provided, the length of the referenced
content is unknown and the entirety of the content can be read.

##### offset

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should we specify that it must be non-negative?

Comment thread LogicalTypes.md Outdated
The annotated group must contain the following fields, identified by name. Field IDs
may also be used for projection:

| Field | Type | Required |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would be better to use repetition instead of required, and either required or optional in the table.

I would also state that the struct must contain exactly these fields, rather than saying they are all required since "required" has a special meaning here (the Parquet type's repetition).

Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
| `path` | STRING | Yes |
| `size` | INT64 | No |
| `offset` | INT64 | No |
| `etag` | STRING | No |

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'm neutral on whether the Parquet spec should stay minimal or include convenient metadata (content type, metadata map etc.) for common use cases

But if we are leaning toward keeping the spec simple, I don't think etag should be part of the spec either. etag feels more like storage specific metadata than a portable field with consistent semantics across storage systems

Would be happy to hear more thoughts on this!

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.

I actually brought this up today in the Parquet sync, it's the odd one out of this pack of properties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I concur. The type should be minimal. Also we should make all fields optional and define the semantics of them missing:

  • path: the file path if present, otherwise this is the path of the current file
  • offset: the start of the binary data, otherwise 0
  • size: the size of the binary data, otherwise the length of the file. size, given or derived, is capped such that offset + size <= file_size

Comment thread src/main/thrift/parquet.thrift Outdated
*
* Annotates a group that represents a reference to an external file.
* The group must contain the following fields identified by name:
* - path (STRING, required): an opaque string path to the file (e.g. s3://bucket/file.jpg)

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.

If it's meant to be a URI, can we mandate it and call the field "uri" instead?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it's not meant to be a URI. it can be anything, but everyone asked for an example, so I provided a URI example. It's up to the clients on what they store and how they read/consume it

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.

Disagreed. The standard should say how the field is to be interpreted. If there is no standard interpretation then this is not a proper spec.

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.

We've gotten pretty far with it just defined as "path" within the Iceberg context. So I would probably be fine with that being the same here although I"m not sure that's actually been useful for Iceberg. I assume most consumers will attempt to parse a URI here anyway and relative paths are also allowed within the URI.

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.

There is no unambiguous way to distinguish between URIs and "paths", so this needs to be specified explicitly.

The example shows an URI, so the spec should mandate an URI.

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.

I think Rfc for URI covers all cases mentioned.
We could specify something like

A URI-reference as defined by RFC 3986, encoded as a Parquet STRING. If absolute, it identifies an external resource. If relative, readers must resolve it against the URI of the Parquet file containing this value using RFC 3986 section 5. If uri is not set, the value refers to the current Parquet file.

Readers may support an implementation-defined set of URI schemes. If the scheme is unsupported or the base URI is unavailable for a relative reference, the value is not resolvable.

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 think it should be left to engines. e.g iceberg could choose to resolve relative paths against table's base location. Other engines might do it differently.

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.

URIs can be relative.

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.

To answer the "it can be left to engines" argument: this is fine for closed environments where the Parquet file never leaves some company cloud of sorts (and even there, several Parquet implementations may be involved).

But Parquet files are a widespread way of publishing and sharing columnar data. If FILE columns cannot be interpreted unambiguously, then ingesting a Parquet file needs additional hands-holding from the user.

@rok rok Jul 9, 2026

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.

I don't understand the push against uri. A path is a subset of URI, ARN is a URI, database path is an URI. URI comes with the contract that it is a a string identifying a resource. If the idea is to add a free form metadata field then path is not the correct name either.

Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
Comment thread src/main/thrift/parquet.thrift
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
| – | set | set | – | external `path`, `[offset, EOF)` |
| – | set | – | set | external `path`, `[0, size)` |
| – | set | set | set | external `path`, `[offset, offset + size)` |
| – | – | set | – | this file, `[offset, EOF)` (self-reference) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would be invalid with the new requirements for size.

Comment thread LogicalTypes.md Outdated
|----------|--------|----------|--------|----------------------------------------------|
| set | – | – | – | the inline bytes |
| – | set | – | – | whole external file at `path` |
| – | set | set | – | external `path`, `[offset, EOF)` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would be invalid with the new requirements for size.

Comment thread LogicalTypes.md Outdated
| `inline` | `path` | `offset` | `size` | Resolves to |
|----------|--------|----------|--------|----------------------------------------------|
| set | – | – | – | the inline bytes |
| – | set | – | – | whole external file at `path` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would be invalid with the new requirements for size.

Comment thread LogicalTypes.md Outdated
Comment on lines +732 to +733
The referenced bytes are compressed with the same `CompressionCodec` as the one
specified for the `inline` column.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's make this clearer.

Suggested change
The referenced bytes are compressed with the same `CompressionCodec` as the one
specified for the `inline` column.
The bytes referenced by a self-reference are compressed with the same `CompressionCodec` as the one specified for the `inline` column.

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 point out CompressionCodec can differ per page?

@brkyvz

brkyvz commented Jul 8, 2026

Copy link
Copy Markdown
Author

@etseidl @sfc-gh-sgrafberger @alkis @rdblue addressed your comments, can you please take another look?

Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread src/main/thrift/parquet.thrift Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md Outdated
Comment thread LogicalTypes.md
Comment thread LogicalTypes.md Outdated
Comment on lines +733 to +735
`size` must be set whenever `offset` is set or `path` is not set, so a self-reference
and any offset-based read always carry an explicit `size`. It may be omitted only for a
whole-file external reference, where the range runs to the end of the referenced file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Update this to match.

Comment thread LogicalTypes.md Outdated

@etseidl etseidl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @brkyvz, this looks good to me now.

@sfc-gh-sgrafberger sfc-gh-sgrafberger left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thank you @brkyvz! Looks good to me.

Comment thread LogicalTypes.md
|----------|--------|----------|--------|-------------------------------------------------------|
| set | – | – | – | the inline bytes |
| – | set | – | – | whole external file at `path` |
| – | set | set | - | external `path`, `[offset, EOF)` |

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.

As per text below ("size must be set whenever offset is set, so any offset-based read always carries an explicit size") this should be invalid?

Suggested change
|| set | set | - | external `path`, `[offset, EOF)` |
|| set | set | - | external `path`, invalid |

Comment thread LogicalTypes.md
If not set, readers must treat the value as 0.
If set and non-zero, readers must seek to this offset to retrieve the referenced data.
`offset` must be set for a self-reference (`path` not set); it is optional for an
external reference (`path` set).

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 worth specifying?

Suggested change
external reference (`path` set).
external reference (`path` set). `offset must not be < 0.

dejankrak-db added a commit to dejankrak-db/delta that referenced this pull request Jul 9, 2026
…a-io#585)

Align with the latest apache/parquet-format#585:
- size must be set whenever offset is set; a self-reference (no path) must set
  offset, and therefore size. Drop the now-invalid [offset, EOF) and [0, size)
  self-reference modes and the external [offset, EOF) mode from the resolution
  table; add explicit invalid rows.
- Define "set" (present, non-null, non-empty for strings) and allow sparse
  group definitions (a group need only define the fields it uses); add an
  inline-only example group.
- Fields matched case-sensitively by name; field IDs "if they exist".
- Readers should ignore unknown checksum algorithms.

Follows the consistent prose intent of PR delta-io#585; note its resolution table still
lists an [offset, EOF) row that contradicts its own validation section (offset
requires size) -- to be raised on the Parquet PR.

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.