SubstraitTypeSystem.getMaxPrecision returns 6 for INTERVAL_DAY, INTERVAL_YEAR and INTERVAL_YEAR_MONTH alongside the datetime names. For an interval Calcite splits the two dimensions: getMaxPrecision bounds the leading field (IntervalSqlType.getPrecision() → SqlIntervalQualifier.getStartPrecision) and getMaxScale bounds the fractional second (getScale() → getFractionalSecondPrecision). The P of a Substrait interval_day<P> is the fractional-second one, so the override constrains the dimension no Substrait type carries and leaves the one it does at Calcite's default.
The spec gives INTERVAL_DAY<P> a range of [-3,650,000..3,650,000] days, which needs 7 leading digits, so a cap of 6 puts the top of the type's own range out of reach — at any spelling:
SELECT INTERVAL '3650000' DAY(7) -> Interval leading field precision '7' out of range for INTERVAL DAY(7)
SELECT INTERVAL '3650000' DAY(6) -> Interval field value 3’650’000 exceeds precision of DAY(6) field
SELECT CAST(NULL AS INTERVAL DAY(7)) -> Interval leading field precision '7' out of range for INTERVAL DAY(7)
SELECT CAST(NULL AS INTERVAL DAY(10) TO SECOND(9)) -> OK, interval_day<9>
SELECT CAST(NULL AS INTERVAL DAY(11) TO SECOND(9)) -> Interval leading field precision '11' out of range
INTERVAL_DAY_SECOND — the SqlTypeName isthmus actually produces for interval_day<P> — is the one name the switch does not cover, so it keeps Calcite's default of 10 while the bare-day spelling gets 6. The year side is safe by luck rather than by design: ±10,000 years is 5 digits, so 6 has room.
Nothing in either conversion direction consults the entries. Calcite→Substrait takes creator.intervalDay(type.getScale()); Substrait→Calcite checks getMaxScale(SqlTypeName.INTERVAL_DAY_SECOND) since #1120 and builds its qualifier with the start precision unspecified, so a converted interval_day<P> already carries a leading precision of 10 (interval_day<3> → INTERVAL DAY TO SECOND(3), precision 10, scale 3). interval_year carries no precision at all. git log -S puts all three cases in the initial commit ae76dfa9 (2022-04-09), before interval_day carried a P.
There is a second effect the SQL above does not show: getStartPrecision defaults to getMaxPrecision(typeName()) when the qualifier leaves it unspecified, so the override also decides what a bare interval carries — a plain DAY qualifier resolves to startPrecision=6 under SubstraitTypeSystem and 10 under Calcite's default. Two spellings of the same Substrait type therefore disagree inside one plan. The emitted Substrait type is unaffected either way, because the fractional-second default is Calcite's own DEFAULT_INTERVAL_FRACTIONAL_SECOND_PRECISION of 6 rather than the override (getDefaultScale(INTERVAL_DAY) is 6 under both type systems). The two numbers coinciding at 6 is what makes this easy to misread as the override reaching the Substrait side.
Dropping the three interval cases looks like the fix, rather than extending the switch to INTERVAL_DAY_SECOND: extending it would lower the cap on the spelling isthmus produces from 10 to whatever value is picked, reintroducing the rejection on the path that matters. Dropping restores Calcite's 10, which covers the spec's 7 digits with room. The datetime cases stay — requireSupportedPrecision genuinely consults getMaxPrecision(TIME | TIMESTAMP | TIMESTAMP_WITH_LOCAL_TIME_ZONE), and for a datetime type Calcite's precision is the fractional-second dimension. That is the line: precision-as-fractional-seconds keeps its override, precision-as-leading-field loses one it never used. No test pins the interval values; SubstraitTypeSystemTest covers DECIMAL, TIMESTAMP and TIME only.
Same species as #1149, a dead getMaxPrecision entry also dating to the initial commit, but not the same case: there the entry is only dead weight, since Calcite's type factory clamps an over-large datetime precision instead of rejecting it, whereas validateIntervalQualifier throws outright. Split out of #1124, where the dialect-driven version of these limits belongs — this is landable without that seam and is a prerequisite for it, since a dialect declaring an interval limit today would be setting the wrong dimension.
Checked on main @ 85c03c7.
SubstraitTypeSystem.getMaxPrecisionreturns 6 forINTERVAL_DAY,INTERVAL_YEARandINTERVAL_YEAR_MONTHalongside the datetime names. For an interval Calcite splits the two dimensions:getMaxPrecisionbounds the leading field (IntervalSqlType.getPrecision()→SqlIntervalQualifier.getStartPrecision) andgetMaxScalebounds the fractional second (getScale()→getFractionalSecondPrecision). ThePof a Substraitinterval_day<P>is the fractional-second one, so the override constrains the dimension no Substrait type carries and leaves the one it does at Calcite's default.The spec gives
INTERVAL_DAY<P>a range of[-3,650,000..3,650,000]days, which needs 7 leading digits, so a cap of 6 puts the top of the type's own range out of reach — at any spelling:INTERVAL_DAY_SECOND— theSqlTypeNameisthmus actually produces forinterval_day<P>— is the one name the switch does not cover, so it keeps Calcite's default of 10 while the bare-day spelling gets 6. The year side is safe by luck rather than by design: ±10,000 years is 5 digits, so 6 has room.Nothing in either conversion direction consults the entries. Calcite→Substrait takes
creator.intervalDay(type.getScale()); Substrait→Calcite checksgetMaxScale(SqlTypeName.INTERVAL_DAY_SECOND)since #1120 and builds its qualifier with the start precision unspecified, so a convertedinterval_day<P>already carries a leading precision of 10 (interval_day<3>→INTERVAL DAY TO SECOND(3), precision 10, scale 3).interval_yearcarries no precision at all.git log -Sputs all three cases in the initial commitae76dfa9(2022-04-09), beforeinterval_daycarried aP.There is a second effect the SQL above does not show:
getStartPrecisiondefaults togetMaxPrecision(typeName())when the qualifier leaves it unspecified, so the override also decides what a bare interval carries — a plainDAYqualifier resolves tostartPrecision=6underSubstraitTypeSystemand10under Calcite's default. Two spellings of the same Substrait type therefore disagree inside one plan. The emitted Substrait type is unaffected either way, because the fractional-second default is Calcite's ownDEFAULT_INTERVAL_FRACTIONAL_SECOND_PRECISIONof 6 rather than the override (getDefaultScale(INTERVAL_DAY)is 6 under both type systems). The two numbers coinciding at 6 is what makes this easy to misread as the override reaching the Substrait side.Dropping the three interval cases looks like the fix, rather than extending the switch to
INTERVAL_DAY_SECOND: extending it would lower the cap on the spelling isthmus produces from 10 to whatever value is picked, reintroducing the rejection on the path that matters. Dropping restores Calcite's 10, which covers the spec's 7 digits with room. The datetime cases stay —requireSupportedPrecisiongenuinely consultsgetMaxPrecision(TIME | TIMESTAMP | TIMESTAMP_WITH_LOCAL_TIME_ZONE), and for a datetime type Calcite's precision is the fractional-second dimension. That is the line: precision-as-fractional-seconds keeps its override, precision-as-leading-field loses one it never used. No test pins the interval values;SubstraitTypeSystemTestcoversDECIMAL,TIMESTAMPandTIMEonly.Same species as #1149, a dead
getMaxPrecisionentry also dating to the initial commit, but not the same case: there the entry is only dead weight, since Calcite's type factory clamps an over-large datetime precision instead of rejecting it, whereasvalidateIntervalQualifierthrows outright. Split out of #1124, where the dialect-driven version of these limits belongs — this is landable without that seam and is a prerequisite for it, since a dialect declaring an interval limit today would be setting the wrong dimension.Checked on
main@ 85c03c7.