Skip to content

feat(operator): read a Parquet file as a source - #8512

Draft
kz930 wants to merge 2 commits into
apache:mainfrom
kz930:feat/parquet-source
Draft

feat(operator): read a Parquet file as a source#8512
kz930 wants to merge 2 commits into
apache:mainfrom
kz930:feat/parquet-source

Conversation

@kz930

@kz930 kz930 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

A source that reads a Parquet file. Texera read CSV, JSONL, Arrow and plain text off disk but not Parquet, and converting one to CSV first loses what the file knew: the column written as an INTEGER came back as text for the schema to guess at again.

The format states its own types in a footer, so this source infers nothing. It reads a row group at a time rather than the whole file, which is the thing a columnar format is chosen to avoid, and a column that is a group, a list or a map is refused by name instead of being dropped in silence.

A timestamp is read with UTC arithmetic, matching what ArrowUtils means by a Texera TIMESTAMP: the count from the epoch lands on a wall clock, and no zone of the machine's own enters it. Reading it any other way would move the value and part the engine from the script the export writes.

No new dependency. parquet-hadoop and parquet-column are already on the classpath under iceberg-parquet, and the reader takes a LocalInputFile, so none of Hadoop's own file plumbing is involved.

Any related issues, documentation, discussions?

Not part of #8325: nothing here makes a workflow exportable. It does ship standalone code, so it reads the trait that issue introduced.

It stays a draft until the set in #8325 has merged, together with the two other new operators, rather than because anything here is unfinished: the trait it reads landed with #8502, and this branch builds and tests green on main today.

Closes #8511, the task this change is the whole of.

How was this PR tested?

Thirteen tests in the operator's own spec, over a Parquet file the spec writes: the columns the footer states, the values read back, a row that wrote no field at all, the timestamp that a zone could have moved, a nested column refused by name, a file that is not Parquet, and the Python the export emits with each scan window.

The parity harness needs no per-operator code for it. A scan source is fixtured by the format it declares, so an encoder for "Parquet" is the whole of what it asks for, and that entry lands with the harness in #8364. With both in place the operator runs on the canonical table both ways and the two answers match.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 5)

🤖 Generated with Claude Code

Texera read CSV, JSONL, Arrow and plain text off disk but not Parquet,
the format most tables in a data-science workflow are already stored in.
Converting one to CSV first loses what the file knew: the column written
as an INTEGER came back as text for the schema to guess at again.

The file states its own types in a footer, so this source infers nothing.
It reads a row group at a time rather than the whole file, which is the
thing a columnar format is chosen to avoid, and a column that is a group,
a list or a map is refused by name instead of being dropped in silence.

A timestamp is read with UTC arithmetic, matching what ArrowUtils means by
a Texera TIMESTAMP: the count from the epoch lands on a wall clock, and no
zone of the machine's own enters it. Reading it any other way would move
the value and part the engine from the script the export writes.

No new dependency: parquet-hadoop and parquet-column are already on the
classpath under iceberg-parquet, and the reader takes a LocalInputFile so
none of Hadoop's own file plumbing is involved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added feature frontend Changes related to the frontend GUI common labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @aglinxinyuan
    You can notify them by mentioning @aglinxinyuan in a comment.

@codecov-commenter

codecov-commenter commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.51%. Comparing base (75c85aa) to head (04036a0).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8512      +/-   ##
============================================
+ Coverage     93.62%   95.51%   +1.89%     
+ Complexity     4857      459    -4398     
============================================
  Files          1212      586     -626     
  Lines         50037    33449   -16588     
  Branches       6132     4151    -1981     
============================================
- Hits          46847    31950   -14897     
+ Misses         1676      936     -740     
+ Partials       1514      563     -951     
Flag Coverage Δ *Carryforward flag
access-control-service 80.18% <ø> (ø)
agent-service 99.32% <ø> (ø) Carriedforward from 62cf952
amber 90.82% <ø> (+0.96%) ⬆️
computing-unit-managing-service 77.14% <ø> (ø)
config-service 87.12% <ø> (ø)
file-service 83.65% <ø> (ø) Carriedforward from 62cf952
frontend 96.15% <ø> (-0.01%) ⬇️
notebook-migration-service 83.73% <ø> (ø)
pyamber 98.47% <ø> (ø) Carriedforward from 62cf952
workflow-compiling-service 74.09% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 5 better · 🔴 3 worse · ⚪ 7 noise (<±5%) · 0 without baseline

Compared against main 75c85aa benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 526 0.321 18,782/26,446/26,446 us 🔴 +17.7% / 🔴 +69.0%
🟢 bs=100 sw=10 sl=64 1,186 0.724 82,959/94,849/94,849 us 🟢 -36.1% / 🟢 -19.5%
bs=1000 sw=10 sl=64 1,347 0.822 738,829/788,919/788,919 us ⚪ within ±5% / 🟢 +26.4%
Baseline details

Latest main 75c85aa from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 526 tuples/sec 583 tuples/sec 794.98 tuples/sec -9.8% -33.8%
bs=10 sw=10 sl=64 MB/s 0.321 MB/s 0.356 MB/s 0.485 MB/s -9.8% -33.8%
bs=10 sw=10 sl=64 p50 18,782 us 15,963 us 12,443 us +17.7% +50.9%
bs=10 sw=10 sl=64 p95 26,446 us 25,755 us 15,648 us +2.7% +69.0%
bs=10 sw=10 sl=64 p99 26,446 us 25,755 us 19,794 us +2.7% +33.6%
bs=100 sw=10 sl=64 throughput 1,186 tuples/sec 1,066 tuples/sec 1,034 tuples/sec +11.3% +14.7%
bs=100 sw=10 sl=64 MB/s 0.724 MB/s 0.65 MB/s 0.631 MB/s +11.4% +14.7%
bs=100 sw=10 sl=64 p50 82,959 us 89,637 us 97,860 us -7.4% -15.2%
bs=100 sw=10 sl=64 p95 94,849 us 148,502 us 104,570 us -36.1% -9.3%
bs=100 sw=10 sl=64 p99 94,849 us 148,502 us 117,811 us -36.1% -19.5%
bs=1000 sw=10 sl=64 throughput 1,347 tuples/sec 1,351 tuples/sec 1,066 tuples/sec -0.3% +26.4%
bs=1000 sw=10 sl=64 MB/s 0.822 MB/s 0.824 MB/s 0.651 MB/s -0.2% +26.3%
bs=1000 sw=10 sl=64 p50 738,829 us 735,998 us 958,266 us +0.4% -22.9%
bs=1000 sw=10 sl=64 p95 788,919 us 773,147 us 998,734 us +2.0% -21.0%
bs=1000 sw=10 sl=64 p99 788,919 us 773,147 us 1,032,953 us +2.0% -23.6%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,379.93,200,128000,526,0.321,18781.91,26445.59,26445.59
1,100,10,64,20,1686.64,2000,1280000,1186,0.724,82959.09,94849.40,94849.40
2,1000,10,64,20,14848.28,20000,12800000,1347,0.822,738829.22,788919.14,788919.14

@kz930

kz930 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

@carloea2 a new source, still a draft like the other two new operators: it reads a Parquet file, taking the column types from the file's own footer rather than inferring them. Would you take a look when you have a moment?

@carloea2 carloea2 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.

Two data-loss cases reproduced with real Parquet files and the compiled native reader.

A DECIMAL column was read as the integer it is stored in, so a file
meaning 12.34 arrived as 1234. It is read as the number the scale makes
of it, in each of the three storages Parquet allows for one, and the
exported script casts the column pandas fills with Decimal objects so
both sides hold the same type.

A timestamp counted in microseconds or nanoseconds was rounded to the
millisecond on the way into a java.sql.Timestamp, which holds nanos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common feature frontend Changes related to the frontend GUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read a Parquet file as a source

3 participants