[wip][core][spark][flink] Support TRUNCATE TABLE on format tables - #9330
Draft
sundapeng wants to merge 2 commits into
Draft
[wip][core][spark][flink] Support TRUNCATE TABLE on format tables#9330sundapeng wants to merge 2 commits into
sundapeng wants to merge 2 commits into
Conversation
sundapeng
marked this pull request as draft
August 20, 2026 23:24
The writer already counted the rows and the bytes, the commit already knows which partitions it wrote, and the catalog now has somewhere to put both. This connects them, behind format-table.commit.report-partition-statistics. Whether a commit replaces or adds follows from what it did to the partition. An appending commit saw only its own files, so it reports an increment: a Flink sink commits once per writer subtask, and N increments over one partition add up to what the job wrote. An overwriting commit replaced everything the partitions held, so what it wrote is the total and it reports that. Static prefix overwrite is why a pure increment cannot express this. Clearing a prefix empties every partition beneath it, including ones this commit writes nothing to; their old data is gone and no increment says so. Those partitions report zero — an exact zero, they really are empty — dated to this commit, since a time reported as unknown would leave the stored one describing files that are gone. They ride in the same create request as the written ones, since a statistic can only be reported for a partition its own request registers. Nothing here unregisters a partition, whatever the numbers say. Only the files this commit actually deleted count as emptying a partition: one another writer removed first is not this commit's doing, and counting it as such would have every concurrent writer report the whole subtree. What a commit wrote into a partition is folded the way PartitionEntry already folds the same five fields: an immutable PartitionStatistics per file, merged into a map by spec. A count nobody took leaves that field unknown for the whole partition rather than reporting the sum of the rest as exact. Reporting is unconditional, the way a Paimon table's commit reports through commitSnapshot. An increment can drift, from a job that retries or a writer that is not Paimon, and a field cannot be written back to unknown once it is set; what converges it is a later full measurement over the same partition. Tests: FormatTableCommitStatisticsTest covers append, dynamic overwrite, static prefix overwrite of a partition this commit does not write, a directory that is no partition of this table being left alone rather than failing the commit, the summation of the independent increments of concurrent writers of one partition, a listing that answers under another scheme, an uncounted file between two counted ones so that unknown has to stay unknown, a report the catalog refuses taking the commit down with it and the written file with it, the same for an overwrite that has already deleted what the partition held, and the numbers reaching the catalog through the write builder.
TRUNCATE TABLE on a Format Table was rejected in the planner, because PaimonFormatTable did not implement TruncatableTable, and TRUNCATE TABLE ... PARTITION reached PaimonPartitionManagement, which serves only FileStoreTable and failed there with "Only FileStoreTable supports partitions." Both FormatTableCommit entry points threw an empty UnsupportedOperationException, and Flink's FlinkFormatTableSink did not implement SupportsTruncate either. FormatTableCommit now removes the data files, reusing the primitive that INSERT OVERWRITE already clears partition directories with. Only data files go: the partition directories stay, and so do their catalog registrations, so SHOW PARTITIONS returns what it returned before (SPARK-34418). What the emptied partitions hold is reported to the catalog as an exact zero, replacing the statistics rather than adjusting them, so a truncated partition stops describing files that are gone. Truncation states that a partition holds nothing whoever deleted the files, so one that was already empty reports zero as well - unlike an overwrite, which reports only what it removed itself so that concurrent writers do not each claim the whole subtree. Which partitions the table has is answered by whatever the table reads its partitions from. For a catalog-managed table that is the catalog, so truncating the whole table empties the registered partitions and leaves a directory still awaiting MSCK REPAIR TABLE alone; for filesystem partition discovery the directory is the answer, and truncating clears the partition levels below the table root. Truncating a named partition the table does not have is an error, reported as Spark expects it per entry point. Flink has no TRUNCATE TABLE ... PARTITION, so FlinkFormatTableSink implements only whole-table truncation.
sundapeng
force-pushed
the
feat/format-table-truncate
branch
from
August 20, 2026 23:59
f710014 to
94ed7ad
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
TRUNCATE TABLEdid not work on a Format Table, and the two statements failed differently.TRUNCATE TABLE twas rejected in the planner:PaimonFormatTabledoes not implementTruncatableTable, soDataSourceV2Strategyfailed evaluatingasTruncatablewithTable does not support truncates: <table>.TRUNCATE TABLE t PARTITION (...)got further, intoPaimonPartitionManagement, which serves onlyFileStoreTableand failed there withOnly FileStoreTable supports partitions.— a message aboutan internal type, on a table that has partitions and lists them under
SHOW PARTITIONS.Underneath both,
FormatTableCommit.truncateTable()andtruncatePartitions()threw an emptyUnsupportedOperationException, and Flink'sFlinkFormatTableSinkdid not implementSupportsTruncateeither.The workaround was
INSERT OVERWRITE ... PARTITION (...) SELECT ... WHERE false, which does notextend to the whole table: without a static partition, an overwrite deletes only the directories it
actually wrote a file into, so an empty result clears nothing.
Approach
FormatTableCommitdeletes the data files, reusingdeletePreviousDataFile, the primitive a staticINSERT OVERWRITEalready clears partition directories with. Only data files go: the partitiondirectories stay, and so do their catalog registrations, so
SHOW PARTITIONSreturns what itreturned before (SPARK-34418). Staging trees of concurrent writers are left alone, on the same
judgement
FormatTableScanreads with.What the emptied partitions hold is reported to the catalog as an exact zero, dated to the
truncation and carried with
replaceStatistics, so a truncated partition stops describing filesthat are gone. The two operations differ in what they claim: an overwrite reports only the files it
removed itself, so that concurrent writers do not each claim the whole subtree, while truncation
states that the partition holds nothing whoever deleted the files — a partition that was already
empty reports zero as well.
Which partitions the table has is answered by whatever the table reads its partitions from. For a
catalog-managed table that is the catalog, so truncating the whole table empties the registered
partitions and leaves a directory still awaiting
MSCK REPAIR TABLEalone — matching whatTruncateTableCommanddoes for a v1 partitioned table. Under filesystem partition discovery thedirectory is the answer, and truncating clears the partition levels below the table root.
On the Spark side
PaimonFormatTableimplementsTruncatableTableand overridesSupportsAtomicPartitionManagement.truncatePartition(s)rather than taking the statement over inPaimonStrategy:TruncatePartitionExecalready expands a partial spec and refreshes the cache,and managed tables reach truncation through the same interface. Truncating a named partition the
table does not have is an error, reported per entry point as Spark's
TruncateTableSuiteBaseexpects.
Flink has no
TRUNCATE TABLE ... PARTITION, soFlinkFormatTableSinkimplements only whole-tabletruncation.
Tests
FormatTableCommitTest: whole-table truncate under filesystem discovery and on a catalog-managedtable (registered partitions emptied, an unregistered directory untouched), an unpartitioned
table, named partitions, a leading-prefix spec, and the value-only default partition; staging
trees survive throughout.
FormatTableCommitStatisticsTest: named partitions report an exact zero as a total, a partitionthat was already empty reports zero too, a prefix reports the partitions underneath it, and
truncating the table reports every registered partition and no unregistered one.
FormatTablePartitionManagementTest,CatalogManagedPartitionTest: registrations survive,unregistered partitions are refused, a
..value is rejected before anything is deleted, and anend-to-end truncate through the REST catalog.
FormatTableTestBase:TRUNCATE TABLE,TRUNCATE TABLE ... PARTITIONwith a full and a partialspec, and a partition the table does not have.
FormatTableITCase:TRUNCATE TABLEon a partitioned and an unpartitioned Format Table in Flink.API and Format
No format change.
PaimonFormatTablegainsTruncatableTableandFlinkFormatTableSinkgainsSupportsTruncate— both engine-side interfaces already implemented for managed tables.format-table.implementation = engineis unchanged and still does not support truncation.Documentation
docs/docs/spark/sql-write.mdanddocs/docs/flink/sql-write.mdx.