docs(sql-ddl): partition columns must be declared last in CREATE TABLE - #19555
docs(sql-ddl): partition columns must be declared last in CREATE TABLE#19555deepakpanda93 wants to merge 1 commit into
Conversation
hudi-agent
left a comment
There was a problem hiding this comment.
Thanks for the docs update! The change correctly documents that Spark stores partition columns at the end of the table schema and fixes the "Create partitioned table" example so its declaration order matches, with a clear caution covering both the cast-error and silent-misalignment failure modes. The content aligns with the existing :::note and with Spark's actual behavior; please still route to a Hudi committer or PMC member (e.g. @yihua) for final confirmation.
cc @yihua
Spark moves partition columns to the end of the table schema. Declaring one
earlier makes the stored column order differ from what was written, and a
positional INSERT then assigns values to the wrong columns.
Verified on Spark 3.5.7 with Hudi 1.2.0. Declaring
(id, name, price, dt, ts) with PARTITIONED BY (dt) stores the table as
(id, name, price, ts, dt), and `insert into t select 1,'a1',10.0,'2021-03-21',1L`
fails with
[INCOMPATIBLE_DATA_FOR_TABLE.CANNOT_SAFELY_CAST]
Cannot safely cast `ts` "STRING" to "BIGINT"
which is the symptom reported in the issue. Worse, when the shifted columns
happen to be type-compatible there is no error at all: declaring
(id, a, dt, b) as strings and inserting 1,'VALUE_A','VALUE_DT','VALUE_B'
returns exit 0 and stores b='VALUE_DT' with dt='VALUE_B', putting the
partition value in a data column and vice versa.
The example in this section was an instance of that. With schema
(id, name, dt, hh) and PARTITIONED BY (dt), hh trails the partition column,
so Spark stores (id, name, hh, dt) and a reader following the example
silently gets hh='2024-01-01' and dt='10'. Partitioning by both columns
fixes it without touching the schema: dt and hh are then the trailing
columns already, in declaration order, so nothing is reordered and the
positional insert lands correctly. Measured on all four variants of this
schema -- PARTITIONED BY (dt) misplaces values, (dt, hh) and (hh) are
correct, and (hh, dt) is rejected outright by
HoodieSchemaUtils.checkPartitionSchemaOrder.
The multi-field note is unchanged and remains correct.
Applied to next and to every 1.x versioned copy, which all carried the same
broken example. This is a correction rather than an addition, so it follows
the wider backport used in apache#19459 rather than the
next-plus-current convention. Re-ran the decisive pair against the 1.0.2
bundle to confirm the older releases behave identically: PARTITIONED BY (dt)
stores (id, name, hh, dt) and yields dt='10', hh='2024-01-01', while
(dt, hh) leaves the order untouched and lands correctly.
The 0.14.x and 0.15.x copies carry the same broken example and are left
alone as end-of-life.
Closes apache#17357.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
63262ab to
c6f6796
Compare
|
Widened the scope in c6f6796, taking up the "happy to widen if preferred" offer in the description. Now 6 files instead of 2. WhyAuditing the other versioned copies showed every one of the ten carries the identical broken example — That puts it in the correction bucket rather than the addition bucket. The next-plus-current convention (#19473) is for additive guidance; a wrong example that silently corrupts data is closer to #19459, which backported a stale default to every affected 1.x version. The cause is Spark's partition-column reordering rather than anything Hudi-version-specific, so the example is wrong on every release that ships it, and those pages are all live. What changedThe corrected example and the caution are now applied to next, 1.2.0, 1.1.1, 1.0.2, 1.0.1 and 1.0.0. All six 0.14.0/1/2 and 0.15.0/1 are deliberately left alone as end-of-life — flagged here rather than left as a silent gap, since they carry the same broken example. Happy to include them if the project would rather every live page be correct. Verified on 1.0.2 before backportingSince the change now touches older releases, I re-ran the decisive pair against
Identical to 1.2.0. (The first 1.0.2 run also threw an unrelated AlsoBumped Build is clean with the warning set byte-identical to a baseline of the same base commit. Rendering was checked on |
hudi-agent
left a comment
There was a problem hiding this comment.
Thanks for the docs update! This change fixes the Create partitioned table example (declaring hh as a partition column so no silent schema reordering occurs) and adds a well-scoped :::caution explaining Spark's partition-column-last behavior with both the cast-error and silent-wrong-column failure modes. The technical claims match the documented reproduction and the fix is applied consistently across the next and versioned docs; please route to a Hudi committer or PMC member for final confirmation.
cc @yihua
Describe the issue this Pull Request addresses
Closes #17357. (JIRA: HUDI-8827, a subtask of HUDI-9109 "Bridging Hudi Spark SQL behavior gaps".)
Spark moves partition columns to the end of the table schema. If a partition column is declared anywhere else in
CREATE TABLE, the stored column order silently differs from what was written, and a positionalINSERT INTO ... SELECTassigns values to the wrong columns. The reporter hit this as a baffling cast error on atscolumn they had explicitly cast to
bigint. Nothing on the SQL DDL page mentions the constraint.The example in the Create partitioned table section had exactly that shape, so following it produced the bug.
Summary and Changelog
Two changes to the Create partitioned table section:
(id, name, dt, hh)withPARTITIONED BY (dt), leavinghhafter the partitioncolumn. Changed the clause to
PARTITIONED BY (dt, hh). The schema is untouched — with both columns partitioned theyare already the trailing columns, in declaration order, so nothing is reordered.
:::cautionstating that partition columns must be declared last, with the resulting schema, bothfailure modes, and the explicit-column-list alternative.
The existing multi-field
:::noteis unchanged; testing confirmed it was already correct.Applied to
website/docs/sql_ddl.md(next) andwebsite/versioned_docs/version-1.2.0/sql_ddl.md(current releaseddocs), per the next-plus-current convention used in #19473 and #19551. The section is byte-identical in 1.0.0–1.1.1 and
carries the same example; those were left alone — happy to widen if preferred.
Reproduction
Spark 3.5.7,
hudi-spark3.5-bundle_2.12:1.2.0.The issue as reported. Declaring
(id, name, price, dt, ts)withPARTITIONED BY (dt)stores the table as(id, name, price, ts, dt), and a positional insert fails:The worse case, which the issue does not mention. When the shifted columns happen to be type-compatible there is no
error at all. Declaring
(id, a, dt, b)as strings and inserting1,'VALUE_A','VALUE_DT','VALUE_B'returns exit 0 andstores
b='VALUE_DT'withdt='VALUE_B'— the partition value written into a data column and a data value used as thepartition. That is why the new text is a
cautionand calls out the silent case explicitly.All four variants of the example's own schema
(id BIGINT, name STRING, dt STRING, hh STRING), inserting1,'n1','2024-01-01','10':PARTITIONED BY(dt)— what the page hadid, name, **hh, dt**hh='2024-01-01',dt='10', exit 0(dt, hh)— what the page now hasid, name, dt, hh(unchanged)dt='2024-01-01',hh='10'(hh)id, name, dt, hh(unchanged)hhis already last(hh, dt)Partition schema fields order does not match the table schema fields order, tableSchemaFields: (dt, hh), partitionFields: (hh, dt)(HoodieSchemaUtils.checkPartitionSchemaOrder)Two things follow. Only the first row was broken, which is why this PR changes one line rather than restructuring the
section. And the
(hh)row shows the constraint is "partition columns must be trailing" in general, not somethingspecific to
dt.Also verified that naming the columns explicitly avoids the mismatch even on a mis-declared table:
INSERT INTO t (id, name, price, dt, ts) SELECT ...lands every value correctly. That is why the caution offers it as analternative.
Worth noting the last row for contrast: getting the
PARTITIONED BYorder wrong fails loudly, whereas getting thedeclaration order wrong — the subject of this PR — is checked by nothing.
A note on scope
The referenced PR #12577 was closed unmerged; it only added a test reproducing the problem, with all three insert
variants commented out as "None of these queries work". So there is no code fix to describe here — this documents
current behaviour, which is what the issue asks for.
Site verification
npm run buildpasses with the warning set byte-identical to a baseline build of the same base commit — no new brokenlinks or anchors.
/docs/sql_ddland/docs/next/sql_ddlwere loaded fromnpm run serveand the code block renders asPARTITIONED BY (dt, hh);with the schema unchanged, the caution renders as an admonition, and the section anchors areintact.
/docs/1.1.1/sql_ddlstill shows the old text, as intended.Impact
Documentation only. No code, config, or behaviour change.
Risk Level
none
Documentation Update
This PR is the documentation update — the SQL DDL page,
/docs/sql_ddland/docs/next/sql_ddl.Contributor's checklist